From 61307a1cb0d1e5ce80f6094fde3e5364baff7fe8 Mon Sep 17 00:00:00 2001
From: Sebastien Robin <seb@nexedi.com>
Date: Mon, 26 Sep 2016 15:21:31 +0000
Subject: [PATCH] ERP5ProjectDistributor: allow to configure the quantity of
 test suite per test node on distributor

---
 .../property_sheet_list.xml                   |  3 +
 .../UnitTestDistributor.xml                   | 66 +++++++++++++++
 .../max_test_suite_property.xml               | 34 ++++++++
 .../Base_viewTestResultFieldLibrary.xml       |  1 +
 .../my_view_mode_max_test_suite.xml           | 84 +++++++++++++++++++
 .../ERP5ProjectUnitTestDistributor_view.xml   |  1 +
 .../my_max_test_suite.xml                     | 72 ++++++++++++++++
 .../test.erp5.testTaskDistribution.py         | 21 +++++
 .../template_portal_type_property_sheet_list  |  1 +
 .../bt/template_property_sheet_id_list        |  3 +-
 .../ERP5ProjectUnitTestDistributor.py         | 14 ++--
 11 files changed, 294 insertions(+), 6 deletions(-)
 create mode 100644 bt5/erp5_test_result/PropertySheetTemplateItem/portal_property_sheets/UnitTestDistributor.xml
 create mode 100644 bt5/erp5_test_result/PropertySheetTemplateItem/portal_property_sheets/UnitTestDistributor/max_test_suite_property.xml
 create mode 100644 bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/Base_viewTestResultFieldLibrary/my_view_mode_max_test_suite.xml
 create mode 100644 bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/ERP5ProjectUnitTestDistributor_view/my_max_test_suite.xml

diff --git a/bt5/erp5_test_result/PortalTypePropertySheetTemplateItem/property_sheet_list.xml b/bt5/erp5_test_result/PortalTypePropertySheetTemplateItem/property_sheet_list.xml
index a44f5c48e6..af15d493cb 100644
--- a/bt5/erp5_test_result/PortalTypePropertySheetTemplateItem/property_sheet_list.xml
+++ b/bt5/erp5_test_result/PortalTypePropertySheetTemplateItem/property_sheet_list.xml
@@ -7,6 +7,9 @@
  <portal_type id="Benchmark Result Line">
   <item>Task</item>
  </portal_type>
+ <portal_type id="ERP5 Project Unit Test Distributor">
+  <item>UnitTestDistributor</item>
+ </portal_type>
  <portal_type id="ERP5 Scalability Distributor">
   <item>ScalabilityDistributor</item>
  </portal_type>
diff --git a/bt5/erp5_test_result/PropertySheetTemplateItem/portal_property_sheets/UnitTestDistributor.xml b/bt5/erp5_test_result/PropertySheetTemplateItem/portal_property_sheets/UnitTestDistributor.xml
new file mode 100644
index 0000000000..2f0a077db1
--- /dev/null
+++ b/bt5/erp5_test_result/PropertySheetTemplateItem/portal_property_sheets/UnitTestDistributor.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="Property Sheet" module="erp5.portal_type"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_count</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>_mt_index</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>_tree</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>UnitTestDistributor</string> </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>Property Sheet</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="2" aka="AAAAAAAAAAI=">
+    <pickle>
+      <global name="Length" module="BTrees.Length"/>
+    </pickle>
+    <pickle> <int>0</int> </pickle>
+  </record>
+  <record id="3" aka="AAAAAAAAAAM=">
+    <pickle>
+      <global name="OOBTree" module="BTrees.OOBTree"/>
+    </pickle>
+    <pickle>
+      <none/>
+    </pickle>
+  </record>
+  <record id="4" aka="AAAAAAAAAAQ=">
+    <pickle>
+      <global name="OOBTree" module="BTrees.OOBTree"/>
+    </pickle>
+    <pickle>
+      <none/>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_test_result/PropertySheetTemplateItem/portal_property_sheets/UnitTestDistributor/max_test_suite_property.xml b/bt5/erp5_test_result/PropertySheetTemplateItem/portal_property_sheets/UnitTestDistributor/max_test_suite_property.xml
new file mode 100644
index 0000000000..7ab9fbdda7
--- /dev/null
+++ b/bt5/erp5_test_result/PropertySheetTemplateItem/portal_property_sheets/UnitTestDistributor/max_test_suite_property.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="Standard Property" module="erp5.portal_type"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>categories</string> </key>
+            <value>
+              <tuple>
+                <string>elementary_type/int</string>
+              </tuple>
+            </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>max_test_suite_property</string> </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>Standard Property</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/Base_viewTestResultFieldLibrary.xml b/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/Base_viewTestResultFieldLibrary.xml
index 4118fbc64a..c0916c1f4b 100644
--- a/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/Base_viewTestResultFieldLibrary.xml
+++ b/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/Base_viewTestResultFieldLibrary.xml
@@ -84,6 +84,7 @@
                     <value>
                       <list>
                         <string>colored_integer</string>
+                        <string>my_view_mode_max_test_suite</string>
                       </list>
                     </value>
                 </item>
diff --git a/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/Base_viewTestResultFieldLibrary/my_view_mode_max_test_suite.xml b/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/Base_viewTestResultFieldLibrary/my_view_mode_max_test_suite.xml
new file mode 100644
index 0000000000..286b683b45
--- /dev/null
+++ b/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/Base_viewTestResultFieldLibrary/my_view_mode_max_test_suite.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>delegated_list</string> </key>
+            <value>
+              <list>
+                <string>title</string>
+              </list>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>my_view_mode_max_test_suite</string> </value>
+        </item>
+        <item>
+            <key> <string>message_values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>external_validator_failed</string> </key>
+                    <value> <string>The input failed the external validator.</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_integer_field</string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string>Base_viewFieldLibrary</string> </value>
+                </item>
+                <item>
+                    <key> <string>title</string> </key>
+                    <value> <string>Max Test Suite</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/ERP5ProjectUnitTestDistributor_view.xml b/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/ERP5ProjectUnitTestDistributor_view.xml
index 0724d70079..139d003e08 100644
--- a/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/ERP5ProjectUnitTestDistributor_view.xml
+++ b/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/ERP5ProjectUnitTestDistributor_view.xml
@@ -103,6 +103,7 @@
                     <value>
                       <list>
                         <string>my_id</string>
+                        <string>my_max_test_suite</string>
                       </list>
                     </value>
                 </item>
diff --git a/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/ERP5ProjectUnitTestDistributor_view/my_max_test_suite.xml b/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/ERP5ProjectUnitTestDistributor_view/my_max_test_suite.xml
new file mode 100644
index 0000000000..97171d2be5
--- /dev/null
+++ b/bt5/erp5_test_result/SkinTemplateItem/portal_skins/erp5_test_result/ERP5ProjectUnitTestDistributor_view/my_max_test_suite.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>my_max_test_suite</string> </value>
+        </item>
+        <item>
+            <key> <string>message_values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>external_validator_failed</string> </key>
+                    <value> <string>The input failed the external validator.</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_view_mode_max_test_suite</string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string>Base_viewTestResultFieldLibrary</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_test_result/TestTemplateItem/portal_components/test.erp5.testTaskDistribution.py b/bt5/erp5_test_result/TestTemplateItem/portal_components/test.erp5.testTaskDistribution.py
index 219b6f57bd..dba931ccf5 100644
--- a/bt5/erp5_test_result/TestTemplateItem/portal_components/test.erp5.testTaskDistribution.py
+++ b/bt5/erp5_test_result/TestTemplateItem/portal_components/test.erp5.testTaskDistribution.py
@@ -14,6 +14,7 @@ class TestTaskDistribution(ERP5TypeTestCase):
     if getattr(tool, "TestTaskDistribution", None) is None:
       tool.newContent(id="TestTaskDistribution",
            portal_type="ERP5 Project Unit Test Distributor")
+    tool.TestTaskDistribution.setMaxTestSuite(None)
     if getattr(tool, "TestPerformanceTaskDistribution", None) is None:
       tool.newContent(id="TestPerformanceTaskDistribution",
            portal_type="Cloud Performance Unit Test Distributor")
@@ -614,6 +615,26 @@ class TestTaskDistribution(ERP5TypeTestCase):
           [test_node_six, ["six", "seven"]],
           [test_node_seven, ["four", "six"]])
 
+  def test_11b_checkERP5ProjectDistributionWithCustomMaxQuantity(self):
+    """
+    Check that the property max_test_suite on the distributor could
+    be used to customize the quantity of test suite affected per test node
+    """
+    test_node, = self._createTestNode(quantity=1)
+    test_suite_list = self._createTestSuite(quantity=5)
+    self.tool.TestTaskDistribution.setMaxTestSuite(None)
+    self.tic()
+    self._callOptimizeAlarm()
+    self.assertEqual(4, len(set(test_node.getAggregateList())))
+    self.tool.TestTaskDistribution.setMaxTestSuite(1)
+    self._callOptimizeAlarm()
+    self.assertEqual(1, len(set(test_node.getAggregateList())))
+    self.tool.TestTaskDistribution.setMaxTestSuite(10)
+    self._callOptimizeAlarm()
+    self.assertEqual(5, len(set(test_node.getAggregateList())))
+    self.assertEqual(set(test_node.getAggregateList()), 
+                     set([x.getRelativeUrl() for x in test_suite_list]))
+
   def test_12_checkCloudPerformanceOptimizationIsStable(self):
     """
     When we have two test suites and we have two test nodes, we should have
diff --git a/bt5/erp5_test_result/bt/template_portal_type_property_sheet_list b/bt5/erp5_test_result/bt/template_portal_type_property_sheet_list
index aa479b2066..c7de566f60 100644
--- a/bt5/erp5_test_result/bt/template_portal_type_property_sheet_list
+++ b/bt5/erp5_test_result/bt/template_portal_type_property_sheet_list
@@ -2,6 +2,7 @@ Benchmark Result Line | Task
 Benchmark Result | Comment
 Benchmark Result | SortIndex
 Benchmark Result | Task
+ERP5 Project Unit Test Distributor | UnitTestDistributor
 ERP5 Scalability Distributor | ScalabilityDistributor
 Scalability Test Suite | Arrow
 Scalability Test Suite | ScalabilityTestSuite
diff --git a/bt5/erp5_test_result/bt/template_property_sheet_id_list b/bt5/erp5_test_result/bt/template_property_sheet_id_list
index 7053c0b429..9cd2e5db59 100644
--- a/bt5/erp5_test_result/bt/template_property_sheet_id_list
+++ b/bt5/erp5_test_result/bt/template_property_sheet_id_list
@@ -6,4 +6,5 @@ TestSuiteConstraint
 ScalabilityTestSuite
 TestNode
 ScalabilityDistributor
-SlapOSSoftwareReleaseUnitTest
\ No newline at end of file
+SlapOSSoftwareReleaseUnitTest
+UnitTestDistributor
\ No newline at end of file
diff --git a/product/ERP5/Document/ERP5ProjectUnitTestDistributor.py b/product/ERP5/Document/ERP5ProjectUnitTestDistributor.py
index fb80077fbc..b79a2e038b 100644
--- a/product/ERP5/Document/ERP5ProjectUnitTestDistributor.py
+++ b/product/ERP5/Document/ERP5ProjectUnitTestDistributor.py
@@ -91,7 +91,7 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
         else:
           break
 
-  def _checkCurrentConfiguration(self,test_node_list, test_suite_list_to_add):
+  def _checkCurrentConfiguration(self,test_node_list, test_suite_list_to_add, max_test_suite):
     """
     We look at what is already installed and then we remove from the list
     of test suite list to add what is already installed.
@@ -100,7 +100,10 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
     test_suite_list_to_remove = []
     for test_node in test_node_list:
       test_suite_list = test_node.getAggregateList()
-      for test_suite in test_suite_list:
+      for index, test_suite in enumerate(test_suite_list, 1):
+        if index > max_test_suite:
+          test_suite_list_to_remove.append(test_suite)
+          continue
         try:
           test_suite_list_to_add.remove(test_suite)
         except ValueError:
@@ -123,9 +126,10 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
         specialise_uid=self.getUid(), sort_on=[('title','ascending')])]
 
     test_node_list_len = len(test_node_list)
+    max_test_suite = self.getMaxTestSuite(TEST_SUITE_MAX)
     def _optimizeConfiguration(test_suite_list_to_add, level=0,
                                test_node_list_to_optimize=None,
-                               test_suite_max=TEST_SUITE_MAX):
+                               test_suite_max=max_test_suite):
       if test_node_list_to_optimize is None:
         test_node_list_to_optimize = [x for x in test_node_list]
       if test_suite_list_to_add:
@@ -154,7 +158,7 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
     test_suite_score, test_suite_list_to_add = self._getSortedNodeTestSuiteList()
     average_quantity = float(len(test_suite_list_to_add)) / (test_node_list_len or 1)
     test_suite_list_to_remove = self._checkCurrentConfiguration(test_node_list,
-      test_suite_list_to_add)
+      test_suite_list_to_add, max_test_suite)
     self._cleanupTestNodeList(test_node_list, test_suite_list_to_remove)
     _optimizeConfiguration(test_suite_list_to_add)
     # once we removed useless test suite and added needed ones,
@@ -172,7 +176,7 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
         lazy_test_node_list.append(test_node)
     # check on most overloaded test nodes first if we can move some work to lazy
     # test nodes
-    for aggregate_quantity in range(TEST_SUITE_MAX, int_average_quantity, -1):
+    for aggregate_quantity in range(max_test_suite, int_average_quantity, -1):
       if len(lazy_test_node_list) == 0:
         break
       overloaded_test_node_list = [x for x in test_node_list if len(x.getAggregateList()) == aggregate_quantity]
-- 
2.30.9