Run unit tests as indenpendent WAF tasks
authorGustavo J. A. M. Carneiro <gjc@inescporto.pt>
Fri, 12 Jun 2009 12:33:37 +0100
changeset 4531 14a102415139
parent 4530 a75eb3b66882
child 4532 a2ae2b0737dc
Run unit tests as indenpendent WAF tasks
src/contrib/wscript
src/core/test.cc
src/core/test.h
src/core/wscript
src/simulator/wscript
utils/run-tests.cc
wscript
--- a/src/contrib/wscript	Fri Jun 12 12:33:21 2009 +0100
+++ b/src/contrib/wscript	Fri Jun 12 12:33:37 2009 +0100
@@ -16,7 +16,7 @@
                                  "library 'libxml-2.0 >= 2.7' not found")
     conf.sub_config('stats')
 
-    conf.write_config_header('ns3/contrib-config.h', project_root_relative=True)
+    conf.write_config_header('ns3/contrib-config.h', top=True)
 
 def build(bld):
     module = bld.create_ns3_module('contrib', ['simulator', 'common'])
--- a/src/core/test.cc	Fri Jun 12 12:33:21 2009 +0100
+++ b/src/core/test.cc	Fri Jun 12 12:33:37 2009 +0100
@@ -55,6 +55,18 @@
 {
   Get ()->m_verbose = true;
 }
+
+void
+TestManager::PrintTestNames (std::ostream &os)
+{
+  for (TestsCI i = Get ()->m_tests.begin (); i != Get ()->m_tests.end (); i++) 
+    {
+      std::string *testName = (*i).second;
+      os << *testName << std::endl;
+    }
+}
+
+
 std::ostream &
 TestManager::Failure (void)
 {
@@ -95,6 +107,47 @@
   return isSuccess;
 }
 
+bool 
+TestManager::RunTest (std::string name)
+{
+  return Get ()->RealRunTest (name);
+}
+bool 
+TestManager::RealRunTest (std::string name)
+{
+  TestsCI i;
+  
+  for (i = m_tests.begin (); i != m_tests.end (); i++) 
+    {
+      std::string *testName = (*i).second;
+      if (*testName == name) 
+        {
+          break;
+        }
+    }
+  if (i == m_tests.end ())
+    {
+      std::cerr << "Test with name " << name << " not found." << std::endl;
+    }
+  
+  if (!(*i).first->RunTests ()) 
+    {
+      if (m_verbose) 
+        {
+          std::cerr << "FAIL " << name << std::endl;
+        }
+      return false;
+    } 
+  else 
+    {
+      if (m_verbose) 
+        {
+          std::cerr << "PASS "<< name << std::endl;
+        }
+      return true;
+    }
+}
+
 Test::Test (char const *name)
 {
   TestManager::Add (this, name);
--- a/src/core/test.h	Fri Jun 12 12:33:21 2009 +0100
+++ b/src/core/test.h	Fri Jun 12 12:33:37 2009 +0100
@@ -89,12 +89,17 @@
    */
   static bool RunTests (void);
 
+  static bool RunTest (std::string name);
+
+  static void PrintTestNames (std::ostream &os);
+
 private:
   friend class Test;
   static void Add (Test *test, char const *name);
   static std::ostream &Failure (void);
   static TestManager *Get (void);
   bool RealRunTests (void);
+  bool RealRunTest (std::string name);
 
   TestManager ();
   ~TestManager ();
--- a/src/core/wscript	Fri Jun 12 12:33:21 2009 +0100
+++ b/src/core/wscript	Fri Jun 12 12:33:37 2009 +0100
@@ -41,7 +41,7 @@
                                  conf.env['ENABLE_THREADING'],
                                  "<pthread.h> include not detected")
 
-    conf.write_config_header('ns3/core-config.h', project_root_relative=True)
+    conf.write_config_header('ns3/core-config.h', top=True)
 
 def build(bld):
     core = bld.create_ns3_module('core')
--- a/src/simulator/wscript	Fri Jun 12 12:33:21 2009 +0100
+++ b/src/simulator/wscript	Fri Jun 12 12:33:37 2009 +0100
@@ -31,7 +31,7 @@
 
     conf.check(header_name='sys/inttypes.h', define_name='HAVE_SYS_INT_TYPES_H')
 
-    conf.write_config_header('ns3/simulator-config.h', project_root_relative=True)
+    conf.write_config_header('ns3/simulator-config.h', top=True)
 
     if not conf.check(lib='rt', uselib='RT', define_name='HAVE_RT'):
         conf.report_optional_feature("RealTime", "Real Time Simulator",
--- a/utils/run-tests.cc	Fri Jun 12 12:33:21 2009 +0100
+++ b/utils/run-tests.cc	Fri Jun 12 12:33:37 2009 +0100
@@ -25,13 +25,42 @@
 
 int main (int argc, char *argv[])
 {
+  if (argc > 1)
+    {
+      if (std::string (argv[1]) == "--ListTests")
+        {
 #ifdef RUN_SELF_TESTS
-  ns3::PacketMetadata::Enable ();
-  ns3::TestManager::EnableVerbose ();
-  bool success = ns3::TestManager::RunTests ();
-  if (!success)
-    return 1;
+          ns3::TestManager::PrintTestNames (std::cout);
+#endif
+        }
+      else
+        {
+          // run the test named by argv[1]
+#ifdef RUN_SELF_TESTS
+          bool success = ns3::TestManager::RunTest (argv[1]);
+          if (!success)
+            {
+              return 1;
+            }
+#else
+          std::cerr << "Unit tests not enabled" << std::endl;
+          return 1;
+#endif
+        }      
+    }
+  else
+    {
+      // run all tests
+#ifdef RUN_SELF_TESTS
+      ns3::PacketMetadata::Enable ();
+      ns3::TestManager::EnableVerbose ();
+      bool success = ns3::TestManager::RunTests ();
+      if (!success)
+        {
+          return 1;
+        }
 #endif /* RUN_SELF_TESTS */
-
+    }
   return 0;
 }
+
--- a/wscript	Fri Jun 12 12:33:21 2009 +0100
+++ b/wscript	Fri Jun 12 12:33:37 2009 +0100
@@ -146,6 +146,9 @@
     opt.add_option('--regression',
                    help=("Enable regression testing; only used for the 'check' target"),
                    default=False, dest='regression', action="store_true")
+    opt.add_option('--check',
+                   help=("Enable unit testing"),
+                   default=False, dest='check', action="store_true")
     opt.add_option('--regression-generate',
                    help=("Generate new regression test traces."),
                    default=False, dest='regression_generate', action="store_true")
@@ -523,6 +526,8 @@
                                  " (--with-regression-traces configure option)")
         regression.run_regression(bld, regression_traces)
 
+    if Options.options.check:
+        _run_check(bld)
 
 
 def shutdown(ctx):
@@ -561,35 +566,163 @@
 
 check_context = Build.BuildContext
 def check(bld):
-    "run the NS-3 unit tests"
-    Scripting.build(bld)
-    ## generate the trace sources list docs
-    env = wutils.bld.env
-    proc_env = wutils.get_proc_env()
-    try:
-        program_obj = wutils.find_program('print-introspected-doxygen', env)
-    except ValueError: # could happen if print-introspected-doxygen is
-                       # not built because of waf configure
-                       # --enable-modules=xxx
-        pass
-    else:
-        prog = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)).abspath(env)
-        out = open(os.path.join('doc', 'introspected-doxygen.h'), 'w')
-        if subprocess.Popen([prog], stdout=out, env=proc_env).wait():
-            raise SystemExit(1)
-        out.close()
+    """run the NS-3 unit tests (deprecated in favour of --check option)"""
+    raise Utils.WafError("Please run `./waf --check' instead.")
+
+
+class print_introspected_doxygen_task(Task.TaskBase):
+    after = 'cc cxx cc_link cxx_link'
+    color = 'BLUE'
+
+    def __init__(self, bld):
+        self.bld = bld
+        super(print_introspected_doxygen_task, self).__init__(generator=self)
+        
+    def __str__(self):
+        return 'print-introspected-doxygen\n'
+
+    def runnable_status(self):
+        return Task.RUN_ME
 
-    print "-- Running NS-3 C++ core unit tests..."
-    wutils.run_program('run-tests', env, wutils.get_command_template(env))
+    def run(self):
+        ## generate the trace sources list docs
+        env = wutils.bld.env
+        proc_env = wutils.get_proc_env()
+        try:
+            program_obj = wutils.find_program('print-introspected-doxygen', env)
+        except ValueError: # could happen if print-introspected-doxygen is
+                           # not built because of waf configure
+                           # --enable-modules=xxx
+            pass
+        else:
+            prog = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj)).abspath(env)
+            out = open(os.path.join('..', 'doc', 'introspected-doxygen.h'), 'w')
+            if subprocess.Popen([prog], stdout=out, env=proc_env).wait():
+                raise SystemExit(1)
+            out.close()
 
-    if env['ENABLE_PYTHON_BINDINGS']:
-        print "-- Running NS-3 Python bindings unit tests..."
-        wutils.run_argv([env['PYTHON'], os.path.join("utils", "python-unit-tests.py")],
-                        env, proc_env, force_no_valgrind=True)
-    else:
-        print "-- Skipping NS-3 Python bindings unit tests: Python bindings not enabled."
+class run_python_unit_tests_task(Task.TaskBase):
+    after = 'cc cxx cc_link cxx_link'
+    color = 'BLUE'
+
+    def __init__(self, bld):
+        self.bld = bld
+        super(run_python_unit_tests_task, self).__init__(generator=self)
+        
+    def __str__(self):
+        return 'run-python-unit-tests\n'
+
+    def runnable_status(self):
+        return Task.RUN_ME
+
+    def run(self):
+        proc_env = wutils.get_proc_env()
+        wutils.run_argv([self.bld.env['PYTHON'], os.path.join("..", "utils", "python-unit-tests.py")],
+                        self.bld.env, proc_env, force_no_valgrind=True)
 
 
+class run_a_unit_test_task(Task.TaskBase):
+    after = 'cc cxx cc_link cxx_link'
+    color = 'BLUE'
+
+    def __init__(self, bld, name_of_test):
+        self.bld = bld
+        super(run_a_unit_test_task, self).__init__(generator=self)
+        self.name_of_test = name_of_test
+        try:
+            program_obj = wutils.find_program("run-tests", self.bld.env)
+        except ValueError, ex:
+            raise Utils.WafError(str(ex))
+        program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
+        self.program_path = program_node.abspath(self.bld.env)
+
+    def __str__(self):
+        return 'run-unit-test(%s)\n' % self.name_of_test
+
+    def runnable_status(self):
+        return Task.RUN_ME
+
+    def run(self):
+        #print repr([self.program_path, self.name_of_test])
+        try:
+            self.retval = wutils.run_argv([self.program_path, self.name_of_test], self.bld.env)
+        except Utils.WafError:
+            self.retval = 1
+        #print "running test %s: exit with %i" % (self.name_of_test, retval)
+        return 0
+
+class get_list_of_unit_tests_task(Task.TaskBase):
+    after = 'cc cxx cc_link cxx_link'
+    color = 'BLUE'
+
+    def __init__(self, bld):
+        self.bld = bld
+        super(get_list_of_unit_tests_task, self).__init__(generator=self)
+        self.tests = []
+
+    def __str__(self):
+        return 'get-unit-tests-list\n'
+
+    def runnable_status(self):
+        return Task.RUN_ME
+
+    def run(self):
+        try:
+            program_obj = wutils.find_program("run-tests", self.bld.env)
+        except ValueError, ex:
+            raise Utils.WafError(str(ex))
+        program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
+        program_path = program_node.abspath(self.bld.env)
+        proc = subprocess.Popen([program_path, "--ListTests"], stdout=subprocess.PIPE,
+                                env=wutils.get_proc_env())
+        self.tests = [l.rstrip() for l in proc.stdout.readlines()]
+        retval = proc.wait()
+        if retval:
+            return retval
+        test_tasks = []
+        for name_of_test in self.tests:
+            test_tasks.append(run_a_unit_test_task(self.bld, name_of_test))
+        collector = collect_unit_test_results_task(self.bld, list(test_tasks))
+        collector.run_after = list(test_tasks)
+        self.more_tasks = [collector] + test_tasks
+        
+
+class collect_unit_test_results_task(Task.TaskBase):
+    after = 'run_a_unit_test_task'
+    color = 'BLUE'
+
+    def __init__(self, bld, test_tasks):
+        self.bld = bld
+        super(collect_unit_test_results_task, self).__init__(generator=self)
+        self.test_tasks = test_tasks
+
+    def __str__(self):
+        return 'collect-unit-tests-results\n'
+
+    def runnable_status(self):
+        for t in self.run_after:
+            if not t.hasrun:
+                return Task.ASK_LATER
+        return Task.RUN_ME
+
+    def run(self):
+        failed = 0
+        for task in self.test_tasks:
+            if task.retval:
+                failed += 1
+        if failed:
+            print "C++ UNIT TESTS: %i tests passed, %i failed." % (len(self.test_tasks) - failed, failed)
+            return 1
+        else:
+            print "C++ UNIT TESTS: all %i tests passed." % (len(self.test_tasks),)
+            return 0
+
+
+def _run_check(bld):
+    task = get_list_of_unit_tests_task(bld)
+    print_introspected_doxygen_task(bld)
+    if bld.env['ENABLE_PYTHON_BINDINGS']:
+        run_python_unit_tests_task(bld)
 
 
 def check_shell(bld):