Fix closest match for branches in settings, to match only branches
with common root.

Example

stable/* vs stable/1707 = Valid
stable/1704 vs stable/1707 = Valid
master vs stable/1707 = Invalid
master vs stable/* = Invalid

Change-Id: I82775d90379ed28e579d59b04d08f51e707ecf52
Signed-off-by: Jan Srnicek <jsrnicek@cisco.com>
diff --git a/pom.xml b/pom.xml
index edd010e..34afdc3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,5 +73,12 @@
             <version>${Gerrit-ApiVersion}</version>
             <scope>test</scope>
         </dependency>
+        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.10.19</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/src/main/java/io/fd/maintainer/plugin/service/SettingsProvider.java b/src/main/java/io/fd/maintainer/plugin/service/SettingsProvider.java
index 8dc9ca0..7c69e0a 100644
--- a/src/main/java/io/fd/maintainer/plugin/service/SettingsProvider.java
+++ b/src/main/java/io/fd/maintainer/plugin/service/SettingsProvider.java
@@ -16,8 +16,7 @@
 
 package io.fd.maintainer.plugin.service;
 
-import static java.lang.String.format;
-
+import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.config.PluginConfigFactory;
@@ -26,13 +25,17 @@
 import com.google.inject.Singleton;
 import io.fd.maintainer.plugin.service.dto.PluginBranchSpecificSettings;
 import io.fd.maintainer.plugin.util.ClosestMatch;
-import java.util.Optional;
-import java.util.function.Function;
-import javax.annotation.Nonnull;
 import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nonnull;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+
 @Singleton
 public final class SettingsProvider implements ClosestMatch {
 
@@ -42,6 +45,7 @@
     private static final String BRANCH_SECTION = "branch";
 
     private static final String PLUGIN_USER = "pluginuser";
+    private static final String DEFAULT_PLUGIN_USER = "non-existing-user";
 
     private static final String MAINTAINERS_FILE_PATH_REF = "maintainerfileref";
     private static final String DEFAULT_MAINTAINERS_FILE_PATH_REF = "master/HEAD";
@@ -64,6 +68,11 @@
     @Inject
     private PluginConfigFactory cfg;
 
+    @VisibleForTesting
+    SettingsProvider(PluginConfigFactory cfg) {
+        this.cfg = cfg;
+    }
+
     public PluginBranchSpecificSettings getBranchSpecificSettings(@Nonnull final String branchName,
                                                                   @Nonnull final Project.NameKey projectKey) {
 
@@ -72,7 +81,24 @@
                 : RefNames.REFS_HEADS.concat(branchName);
 
         LOG.info("Reading configuration for branch {}", fullBranchName);
-        return getSettingsForBranch(fullBranchName, closesBranchMatch(fullBranchName, projectKey), projectKey);
+        final Optional<String> closestBranch = closesBranchMatch(fullBranchName, projectKey);
+
+        if (closestBranch.isPresent()) {
+            // either current branch or some similar has config
+            return getSettingsForBranch(fullBranchName, closestBranch.get(), projectKey);
+        }
+
+        //not current nor similar branch has config, therefore return default
+        return new PluginBranchSpecificSettings.PluginSettingsBuilder()
+                .setAllowMaintainersSubmit(DEFAULT_ALLOW_SUBMIT)
+                .setAutoAddReviewers(DEFAULT_AUTO_ADD_REVIEWERS)
+                .setAutoSubmit(DEFAULT_AUTO_SUBMIT)
+                .setDislikeWarnings(DEFAULT_DISLIKE_WARNINGS)
+                .setBranch(fullBranchName)
+                .setFileRef(DEFAULT_MAINTAINERS_FILE_REF)
+                .setLocalFilePath(DEFAULT_MAINTAINERS_FILE_PATH_REF)
+                .setPluginUserName(DEFAULT_PLUGIN_USER)
+                .createPluginSettings();
     }
 
     private PluginBranchSpecificSettings getSettingsForBranch(final String branchName, final String closestBranch,
@@ -162,10 +188,60 @@
     }
 
     // match by the number of changes needed to change one String into another
-    private String closesBranchMatch(final String branchName, final Project.NameKey projectKey) {
+    private Optional<String> closesBranchMatch(final String branchName, final Project.NameKey projectKey) {
+        final BranchInfo currentBranchInfo = new BranchInfo(branchName);
         return projectSpecificPluginConfig(projectKey).getSubsections(BRANCH_SECTION).stream()
-                .reduce((branchOne, branchTwo) -> closestMatch(branchName, branchOne, branchTwo))
-                // if non use default
-                .orElse(RefNames.REFS_HEADS);
+                .map(BranchInfo::new)
+                .filter(branchInfo -> branchInfo.isAlternativeFor(currentBranchInfo))
+                .map(BranchInfo::getBranchPart)
+                .reduce((branchOne, branchTwo) -> closestMatch(branchName, branchOne, branchTwo));
+    }
+
+    static class BranchInfo {
+
+        private final boolean isWildcarded;
+        private final boolean isGerritReviewBranch;
+        private final String fullBranchName;
+        private final String branchPart;
+
+        BranchInfo(@Nonnull final String input) {
+            checkNotNull(input, "Input for %s cannot be null", this.getClass().getName());
+            final String[] parts = input.split("\\/");
+            isWildcarded = parts[parts.length - 1].trim().equals("*");
+            isGerritReviewBranch = input.trim().startsWith(RefNames.REFS_HEADS);
+            fullBranchName = input.trim();
+            if (isGerritReviewBranch) {
+                branchPart = fullBranchName.replace(RefNames.REFS_HEADS, "").replace("/*", "");
+            } else {
+                branchPart = fullBranchName;
+            }
+        }
+
+        public boolean isAlternativeFor(final BranchInfo other) {
+            if (this.isGerritReviewBranch && other.isGerritReviewBranch) {
+                // both branches are standard review branches like /refs/heads/master for ex.
+                final String[] thisBranchParts = this.branchPart.split("\\/");
+                final String[] otherBranchParts = other.branchPart.split("\\/");
+
+                return thisBranchParts[0].equals(otherBranchParts[0]);
+            }
+            return fullBranchName.equals(other.fullBranchName);
+        }
+
+        public boolean isWildcarded() {
+            return isWildcarded;
+        }
+
+        public boolean isGerritReviewBranch() {
+            return isGerritReviewBranch;
+        }
+
+        public String getFullBranchName() {
+            return fullBranchName;
+        }
+
+        public String getBranchPart() {
+            return branchPart;
+        }
     }
 }
diff --git a/src/test/java/io/fd/maintainer/plugin/service/BranchInfoTest.java b/src/test/java/io/fd/maintainer/plugin/service/BranchInfoTest.java
new file mode 100644
index 0000000..68e1666
--- /dev/null
+++ b/src/test/java/io/fd/maintainer/plugin/service/BranchInfoTest.java
@@ -0,0 +1,79 @@
+package io.fd.maintainer.plugin.service;
+
+import io.fd.maintainer.plugin.service.SettingsProvider.BranchInfo;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class BranchInfoTest {
+
+    private BranchInfo stableWildcardedInfo;
+    private BranchInfo masterInfo;
+    private BranchInfo stableInfo;
+    private BranchInfo nonReviewBranch;
+
+    @Before
+    public void init() {
+        this.masterInfo = new BranchInfo("refs/heads/master");
+        this.stableInfo = new BranchInfo("refs/heads/stable/1707");
+        this.stableWildcardedInfo = new BranchInfo("refs/heads/stable/*");
+        this.nonReviewBranch = new BranchInfo("non/review/branch");
+    }
+
+    @Test
+    public void testNonReview() {
+        assertEquals("non/review/branch", nonReviewBranch.getBranchPart());
+        assertEquals("non/review/branch", nonReviewBranch.getFullBranchName());
+        assertFalse(nonReviewBranch.isGerritReviewBranch());
+        assertFalse(nonReviewBranch.isWildcarded());
+    }
+
+    @Test
+    public void testMasterProcessing() {
+        assertEquals("master", masterInfo.getBranchPart());
+        assertEquals("refs/heads/master", masterInfo.getFullBranchName());
+        assertFalse(masterInfo.isWildcarded());
+        assertTrue(masterInfo.isGerritReviewBranch());
+    }
+
+    @Test
+    public void testStableProcessing() {
+        assertEquals("stable/1707", stableInfo.getBranchPart());
+        assertEquals("refs/heads/stable/1707", stableInfo.getFullBranchName());
+        assertFalse(stableInfo.isWildcarded());
+        assertTrue(stableInfo.isGerritReviewBranch());
+    }
+
+    @Test
+    public void testWildcaded() {
+        assertEquals("stable", stableWildcardedInfo.getBranchPart());
+        assertEquals("refs/heads/stable/*", stableWildcardedInfo.getFullBranchName());
+        assertTrue(stableWildcardedInfo.isWildcarded());
+        assertTrue(stableWildcardedInfo.isGerritReviewBranch());
+    }
+
+    @Test
+    public void testCompare() {
+        assertFalse(masterInfo.isAlternativeFor(stableInfo));
+        assertFalse(masterInfo.isAlternativeFor(stableWildcardedInfo));
+        assertTrue(masterInfo.isAlternativeFor(masterInfo));
+        assertFalse(masterInfo.isAlternativeFor(nonReviewBranch));
+
+        assertFalse(stableInfo.isAlternativeFor(masterInfo));
+        assertTrue(stableInfo.isAlternativeFor(stableWildcardedInfo));
+        assertTrue(stableInfo.isAlternativeFor(stableInfo));
+        assertFalse(stableInfo.isAlternativeFor(nonReviewBranch));
+
+        assertFalse(stableWildcardedInfo.isAlternativeFor(masterInfo));
+        assertTrue(stableWildcardedInfo.isAlternativeFor(stableInfo));
+        assertTrue(stableWildcardedInfo.isAlternativeFor(stableWildcardedInfo));
+        assertFalse(stableWildcardedInfo.isAlternativeFor(nonReviewBranch));
+
+        assertFalse(nonReviewBranch.isAlternativeFor(masterInfo));
+        assertFalse(nonReviewBranch.isAlternativeFor(stableInfo));
+        assertFalse(nonReviewBranch.isAlternativeFor(stableWildcardedInfo));
+        assertTrue(nonReviewBranch.isAlternativeFor(nonReviewBranch));
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/io/fd/maintainer/plugin/service/SettingsProviderTest.java b/src/test/java/io/fd/maintainer/plugin/service/SettingsProviderTest.java
new file mode 100644
index 0000000..6acf11f
--- /dev/null
+++ b/src/test/java/io/fd/maintainer/plugin/service/SettingsProviderTest.java
@@ -0,0 +1,57 @@
+package io.fd.maintainer.plugin.service;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import io.fd.maintainer.plugin.service.dto.PluginBranchSpecificSettings;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.when;
+
+public class SettingsProviderTest {
+
+    @Mock
+    private PluginConfigFactory cfg;
+
+    private SettingsProvider provider;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        /*
+         * [branch "refs/heads/master"]
+         pluginuser = vppmaintainerplugin
+         maintainerfileref = master
+         maintainerfile = MAINTAINER
+         autoaddreviewers = true
+         allowmaintainersubmit = false
+         autosubmit = false
+         dislikewarnings = false
+         * */
+        Config config = new Config();
+        config.setString("branch", "refs/heads/master", "pluginuser", "vppmaintainerplugin");
+        config.setString("branch", "refs/heads/master", "maintainerfileref", "master");
+        config.setString("branch", "refs/heads/master", "maintainerfile", "MAINTAINER");
+        config.setString("branch", "refs/heads/master", "autoaddreviewers", "true");
+        config.setString("branch", "refs/heads/master", "allowmaintainersubmit", "false");
+        config.setString("branch", "refs/heads/master", "autosubmit", "false");
+        config.setString("branch", "refs/heads/master", "dislikewarnings", "false");
+
+        when(cfg.getProjectPluginConfig(new Project.NameKey("vpp"), "maintainer")).thenReturn(config);
+        provider = new SettingsProvider(cfg);
+    }
+
+    @Test
+    public void getBranchSpecificSettings() throws Exception {
+        PluginBranchSpecificSettings settings = provider.getBranchSpecificSettings("refs/for/stable/1707", new Project.NameKey("vpp"));
+        assertFalse(settings.isAutoAddReviewers());
+        assertFalse(settings.isAllowMaintainersSubmit());
+        assertFalse(settings.isAutoSubmit());
+        assertFalse(settings.isDislikeWarnings());
+    }
+
+}
\ No newline at end of file