From a0c3e7264aad583e956c1608bdc62a634b971f36 Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Thu, 26 Apr 2012 19:35:46 +0200
Subject: [PATCH] QA: Enable use of OR conditions in test checks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Until now β€œTestRunIf” and β€œTestEnabled” could only handle AND. With this
patch a new class named β€œEither” is added to β€œqa_config” and allows OR.
The name β€œEither” was chosen instead of β€œOr” as the latter is very close
to the reserved keyword β€œor”.

Examples:
  ["rapi", Either(["instance-rename", "instance-reboot"])]

  Either(["node-list", "instance-list", "job-list"])

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>
---
 Makefile.am                   |   2 +
 autotools/run-in-tempdir      |   2 +-
 qa/qa_config.py               |  71 ++++++++++++++++--
 test/qa.qa_config_unittest.py | 137 ++++++++++++++++++++++++++++++++++
 4 files changed, 204 insertions(+), 8 deletions(-)
 create mode 100755 test/qa.qa_config_unittest.py

diff --git a/Makefile.am b/Makefile.am
index 31cd8fa5c..4da1ae32e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -498,6 +498,7 @@ PYTHON_BOOTSTRAP = \
 	tools/ensure-dirs
 
 qa_scripts = \
+	qa/__init__.py \
 	qa/ganeti-qa.py \
 	qa/qa_cluster.py \
 	qa/qa_config.py \
@@ -847,6 +848,7 @@ python_tests = \
 	test/ganeti.utils.x509_unittest.py \
 	test/ganeti.utils_unittest.py \
 	test/ganeti.workerpool_unittest.py \
+	test/qa.qa_config_unittest.py \
 	test/cfgupgrade_unittest.py \
 	test/docs_unittest.py \
 	test/pycurl_reset_unittest.py \
diff --git a/autotools/run-in-tempdir b/autotools/run-in-tempdir
index e656ffe73..316b54775 100755
--- a/autotools/run-in-tempdir
+++ b/autotools/run-in-tempdir
@@ -10,7 +10,7 @@ trap "rm -rf $tmpdir" EXIT
 
 mkdir $tmpdir/doc
 
-cp -r autotools daemons scripts lib tools test $tmpdir
+cp -r autotools daemons scripts lib tools test qa $tmpdir
 cp -r doc/examples $tmpdir/doc
 
 mv $tmpdir/lib $tmpdir/ganeti
diff --git a/qa/qa_config.py b/qa/qa_config.py
index e058a71d8..5e19c5eab 100644
--- a/qa/qa_config.py
+++ b/qa/qa_config.py
@@ -60,22 +60,79 @@ def get(name, default=None):
   return cfg.get(name, default)
 
 
-def TestEnabled(tests):
+class Either:
+  def __init__(self, tests):
+    """Initializes this class.
+
+    @type tests: list or string
+    @param tests: List of test names
+    @see: L{TestEnabled} for details
+
+    """
+    self.tests = tests
+
+
+def _MakeSequence(value):
+  """Make sequence of single argument.
+
+  If the single argument is not already a list or tuple, a list with the
+  argument as a single item is returned.
+
+  """
+  if isinstance(value, (list, tuple)):
+    return value
+  else:
+    return [value]
+
+
+def _TestEnabledInner(check_fn, names, fn):
+  """Evaluate test conditions.
+
+  @type check_fn: callable
+  @param check_fn: Callback to check whether a test is enabled
+  @type names: sequence or string
+  @param names: Test name(s)
+  @type fn: callable
+  @param fn: Aggregation function
+  @rtype: bool
+  @return: Whether test is enabled
+
+  """
+  names = _MakeSequence(names)
+
+  result = []
+
+  for name in names:
+    if isinstance(name, Either):
+      value = _TestEnabledInner(check_fn, name.tests, compat.any)
+    elif isinstance(name, (list, tuple)):
+      value = _TestEnabledInner(check_fn, name, compat.all)
+    else:
+      value = check_fn(name)
+
+    result.append(value)
+
+  return fn(result)
+
+
+def TestEnabled(tests, _cfg=None):
   """Returns True if the given tests are enabled.
 
-  @param tests: a single test, or a list of tests to check
+  @param tests: A single test as a string, or a list of tests to check; can
+    contain L{Either} for OR conditions, AND is default
 
   """
-  if isinstance(tests, basestring):
-    tests = [tests]
+  if _cfg is None:
+    _cfg = cfg
 
   # Get settings for all tests
-  all_tests = cfg.get("tests", {})
+  cfg_tests = _cfg.get("tests", {})
 
   # Get default setting
-  default = all_tests.get("default", True)
+  default = cfg_tests.get("default", True)
 
-  return compat.all(all_tests.get(name, default) for name in tests)
+  return _TestEnabledInner(lambda name: cfg_tests.get(name, default),
+                           tests, compat.all)
 
 
 def GetMasterNode():
diff --git a/test/qa.qa_config_unittest.py b/test/qa.qa_config_unittest.py
new file mode 100755
index 000000000..fd7332271
--- /dev/null
+++ b/test/qa.qa_config_unittest.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2012 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Script for testing qa.qa_config"""
+
+import unittest
+
+from qa import qa_config
+
+import testutils
+
+
+class TestTestEnabled(unittest.TestCase):
+  def testSimple(self):
+    for name in ["test", ["foobar"], ["a", "b"]]:
+      self.assertTrue(qa_config.TestEnabled(name, _cfg={}))
+
+    for default in [False, True]:
+      self.assertFalse(qa_config.TestEnabled("foo", _cfg={
+        "tests": {
+          "default": default,
+          "foo": False,
+          },
+        }))
+
+      self.assertTrue(qa_config.TestEnabled("bar", _cfg={
+        "tests": {
+          "default": default,
+          "bar": True,
+          },
+        }))
+
+  def testEitherWithDefault(self):
+    names = qa_config.Either("one")
+
+    self.assertTrue(qa_config.TestEnabled(names, _cfg={
+      "tests": {
+        "default": True,
+        },
+      }))
+
+    self.assertFalse(qa_config.TestEnabled(names, _cfg={
+      "tests": {
+        "default": False,
+        },
+      }))
+
+  def testEither(self):
+    names = [qa_config.Either(["one", "two"]),
+             qa_config.Either("foo"),
+             "hello",
+             ["bar", "baz"]]
+
+    self.assertTrue(qa_config.TestEnabled(names, _cfg={
+      "tests": {
+        "default": True,
+        },
+      }))
+
+    self.assertFalse(qa_config.TestEnabled(names, _cfg={
+      "tests": {
+        "default": False,
+        },
+      }))
+
+    for name in ["foo", "bar", "baz", "hello"]:
+      self.assertFalse(qa_config.TestEnabled(names, _cfg={
+        "tests": {
+          "default": True,
+          name: False,
+          },
+        }))
+
+    self.assertFalse(qa_config.TestEnabled(names, _cfg={
+      "tests": {
+        "default": True,
+        "one": False,
+        "two": False,
+        },
+      }))
+
+    self.assertTrue(qa_config.TestEnabled(names, _cfg={
+      "tests": {
+        "default": True,
+        "one": False,
+        "two": True,
+        },
+      }))
+
+    self.assertFalse(qa_config.TestEnabled(names, _cfg={
+      "tests": {
+        "default": True,
+        "one": True,
+        "two": True,
+        "foo": False,
+        },
+      }))
+
+  def testEitherNestedWithAnd(self):
+    names = qa_config.Either([["one", "two"], "foo"])
+
+    self.assertTrue(qa_config.TestEnabled(names, _cfg={
+      "tests": {
+        "default": True,
+        },
+      }))
+
+    for name in ["one", "two"]:
+      self.assertFalse(qa_config.TestEnabled(names, _cfg={
+        "tests": {
+          "default": True,
+          "foo": False,
+          name: False,
+          },
+        }))
+
+
+if __name__ == "__main__":
+  testutils.GanetiTestProgram()
-- 
GitLab