From ef78011c483b82c13315d974001610d62a6165fc Mon Sep 17 00:00:00 2001
From: Mathieu Courtois <mathieu.courtois@edf.fr>
Date: Fri, 4 Jan 2019 15:29:20 +0100
Subject: [PATCH] [#28360] Improve checking of multiple heads.

--HG--
branch : edf/mc
---
 lib/hgaster/ascommands.py        | 66 ++++++++++++++++++++++----------
 lib/hgaster/ext_utils.py         |  2 +-
 lib/hgaster/hooks/generic.py     |  2 +
 share/test/hgaster/test_hooks.py |  4 +-
 4 files changed, 50 insertions(+), 24 deletions(-)

diff --git a/lib/hgaster/ascommands.py b/lib/hgaster/ascommands.py
index f4da15b0..8fb860ff 100644
--- a/lib/hgaster/ascommands.py
+++ b/lib/hgaster/ascommands.py
@@ -110,7 +110,7 @@ def submit(ui, repo, *args, **opts):
     if reponame == "devtools":
         class_ = SubmitDevtools
     elif reponame in ("data", "src", "validation"):
-        class_ = SubmitAster
+        class_ = SubmitAsterLegacy
     elif reponame in ("data-xx", "src-xx", "validation-xx"):
         class_ = SubmitAsterXX
     elif reponame == "salome-codeaster-study":
@@ -329,7 +329,7 @@ class CandidateRevisionCheck(object):
             if not self._local:
                 self.refrev = self.get_reference_revision()
         if not self._local:
-            if not self.valid_branch(self.repo, self.wrkbranch):
+            if not self.valid_branch():
                 self._error(_("integration is not allowed in the branch '{0}'")
                             .format(self.wrkbranch))
         if not self.refrev:
@@ -580,10 +580,20 @@ class CandidateRevisionCheck(object):
             self._error(_("can not find the last integrated revision"))
         return refrev
 
-    @staticmethod
-    def valid_branch(repo, branch):
+    def check_single_head(self):
+        """Check for single heads in branch."""
+        from hooks.generic import OK, single_head_per_branch_hook
+        checker = single_head_per_branch_hook()
+        return checker(self.ui, self.repo, self.wrkrev) == OK
+
+    def check_branch(self, repo, branch): # pragma pylint: disable=unused-argument
         """Check branch validity."""
-        return valid_branch(repo, branch)
+        raise NotImplementedError("must be subclassed")
+
+    def valid_branch(self):
+        """Check branch validity."""
+        return (self.check_branch(self.repo, self.wrkbranch) and
+                self.check_single_head())
 
 
 class CandidateRevisionCheckAster(CandidateRevisionCheck):
@@ -656,9 +666,8 @@ class CandidateRevisionCheckAster(CandidateRevisionCheck):
             for fname in lfiles:
                 adiff = get_diff(self.ui, self.repo, revs, [fname])
                 diffs[typ][fname] = adiff
-        errcode = check_diff(self._report, diffs)
+        check_diff(self._report, diffs)
         os.chdir(prev)
-        return errcode
 
     @stop_on_failure
     def check_waf_build(self):
@@ -944,7 +953,7 @@ class AbstractSubmit(CandidateRevisionCheck):
         return 0
 
 
-class SubmitAster(CandidateRevisionCheckAster, AbstractSubmit):
+class AbstractSubmitAster(CandidateRevisionCheckAster, AbstractSubmit):
 
     """Check code_aster source files at the parent revision
     and, in case of success, submit changesets for continuous integration.
@@ -952,7 +961,7 @@ class SubmitAster(CandidateRevisionCheckAster, AbstractSubmit):
 
     def __init__(self, ui, repo, *args, **opts):
         """Initialization"""
-        super(SubmitAster, self).__init__(ui, repo, *args, **opts)
+        super(AbstractSubmitAster, self).__init__(ui, repo, *args, **opts)
         self._resutest = opts.get("resutest")
 
     @stop_on_failure
@@ -992,7 +1001,7 @@ class SubmitAster(CandidateRevisionCheckAster, AbstractSubmit):
 
     def run(self):
         """Execute the elementary tasks"""
-        super(SubmitAster, self).check_init()
+        super(AbstractSubmitAster, self).check_init()
         self.set_working_revision()
         self.set_parameters()
         logger.title(_("asking integration in branch '{0}' of the repository"
@@ -1025,7 +1034,24 @@ class SubmitAster(CandidateRevisionCheckAster, AbstractSubmit):
         return 0
 
 
-class SubmitAsterXX(SubmitAster):
+class SubmitAsterLegacy(AbstractSubmitAster):
+
+    """Check code_aster source files at the parent revision
+    and, in case of success, submit changesets for continuous integration.
+    """
+
+    @staticmethod
+    def check_single_head():
+        """Check single heads in branch."""
+        # multiple heads allowed in code_aster legacy repositories
+        return True
+
+    def check_branch(self, repo, branch):
+        """Check branch validity."""
+        return valid_branch(repo, branch)
+
+
+class SubmitAsterXX(AbstractSubmitAster):
 
     """Check code_aster++ source files at the parent revision
     and, in case of success, submit changesets for continuous integration.
@@ -1055,8 +1081,7 @@ class SubmitAsterXX(SubmitAster):
         XXCFG.waf_builddir = "build/std"
         return XXCFG.get(key, default)
 
-    @staticmethod
-    def valid_branch(dummy, branch):
+    def check_branch(self, dummy, branch):
         """Check branch validity."""
         from hooks.generic import OK, branch_checker
         return branch_checker('^asterxx/')(branch) == OK
@@ -1109,22 +1134,21 @@ class SubmitDevtools(AbstractSubmit):
         par = super(SubmitDevtools, self)
         return par.check_issues_status(expected, logfunc=logger.warn)
 
-    @staticmethod
-    def valid_branch(dummy, branch):
+    def check_branch(self, dummy, branch):
         """Check branch validity."""
         from hooks.generic import OK
         from hooks.devtools import repo_branch_checker
         return repo_branch_checker(branch) == OK
 
 
-class SubmitSMecaMinimal(AbstractSubmit):
+class AbstractSubmitSmeca(AbstractSubmit):
 
     """Minimal 'submit' command for some salome_meca tools (only push changes).
     """
 
     def __init__(self, ui, repo, *args, **opts):
         """Initialization"""
-        super(SubmitSMecaMinimal, self).__init__(ui, repo, *args, **opts)
+        super(AbstractSubmitSmeca, self).__init__(ui, repo, *args, **opts)
         self.refbranch = "edf/default"
 
     @classmethod
@@ -1133,7 +1157,7 @@ class SubmitSMecaMinimal(AbstractSubmit):
         return SMCFG.get(key, default)
 
 
-class SubmitAsterStudy(SubmitSMecaMinimal):
+class SubmitAsterStudy(AbstractSubmitSmeca):
 
     """Check AsterStudy source files at the parent revision
     and, in case of success, submit changesets for continuous integration.
@@ -1149,19 +1173,19 @@ class SubmitAsterStudy(SubmitSMecaMinimal):
                     ]
 
     @staticmethod
-    def valid_branch(dummy, branch):
+    def check_branch(dummy, branch):
         """Check branch validity."""
         from hooks.generic import OK
         from hooks.asterstudy import repo_branch_checker
         return repo_branch_checker(branch) == OK
 
 
-class SubmitRTool(SubmitSMecaMinimal):
+class SubmitRTool(AbstractSubmitSmeca):
 
     """Submit command for some salome_meca RTOOL plugin."""
 
     @staticmethod
-    def valid_branch(dummy, branch):
+    def check_branch(dummy, branch):
         """Check branch validity."""
         from hooks.generic import OK
         from hooks.rtool import repo_branch_checker
diff --git a/lib/hgaster/ext_utils.py b/lib/hgaster/ext_utils.py
index d61731d8..9a25641e 100644
--- a/lib/hgaster/ext_utils.py
+++ b/lib/hgaster/ext_utils.py
@@ -205,7 +205,7 @@ def get_hgpath(ui, repo, dest):
 
 
 def valid_branch(repo, branch):
-    """Return True if 'branch' is one of the open branches
+    """Return True if 'branch' is one of the opened branches
     (default or the last vNN)"""
     if branch == 'default':
         return True
diff --git a/lib/hgaster/hooks/generic.py b/lib/hgaster/hooks/generic.py
index 5a0ce331..f15ce050 100644
--- a/lib/hgaster/hooks/generic.py
+++ b/lib/hgaster/hooks/generic.py
@@ -180,6 +180,8 @@ def single_head_per_branch_hook(in_branch=None):
         for branch, heads in repo.branchmap().items():
             if in_branch and not re.search(in_branch, branch):
                 continue
+            if node.branch() != branch:
+                continue
             unclosed = [head for head in heads
                         if not repo[head].extra().get("close")]
             if len(unclosed) > 1:
diff --git a/share/test/hgaster/test_hooks.py b/share/test/hgaster/test_hooks.py
index a2833cc1..53d6c6ee 100644
--- a/share/test/hgaster/test_hooks.py
+++ b/share/test/hgaster/test_hooks.py
@@ -209,7 +209,7 @@ def test_single_head():
         FakeCset(0, branch='default'),
         FakeCset(1, branch='edf/user', head=True),
     ])
-    assert_that(single_head_per_branch(ui, repo, 1,
+    assert_that(single_head_per_branch(ui, repo, repo[1],
                                        hooktype='pretxnchangegroup'),
                 equal_to(OK))
 
@@ -218,7 +218,7 @@ def test_single_head():
         FakeCset(1, branch='edf/user', head=True),
         FakeCset(2, branch='edf/user', head=True),
     ])
-    assert_that(single_head_per_branch(ui, repo, 1,
+    assert_that(single_head_per_branch(ui, repo, repo[1],
                                        hooktype='pretxnchangegroup'),
                 equal_to(NOOK))
 
-- 
GitLab