From a3d856ea013d9060c60fe861a6ea4585337cc69b Mon Sep 17 00:00:00 2001
From: pospelov <pospelov@fz-juelich.de>
Date: Tue, 26 Mar 2013 13:20:40 +0100
Subject: [PATCH] Main file FunctionalTests/test_all.py which runs 4 kind of
 functional tests all together: TestCore, TestPyCore, TestFit, TestPyFit

---
 Tests/FunctionalTests/TestCore/TestCore.py    |  87 +++++++++++
 .../test_all.py => TestFit/TestFit.py}        |  18 +--
 .../FunctionalTests/TestPyCore/TestPyCore.py  |  83 +++++++++++
 Tests/FunctionalTests/TestPyFit/README        |  12 ++
 Tests/FunctionalTests/TestPyFit/TestPyFit.py  |  60 ++++++++
 .../{TestPyCore => TestPyFit}/test_all.py     |  28 +---
 Tests/FunctionalTests/TestPyFit/testfit01.py  | 136 ++++++++++++++++++
 Tests/FunctionalTests/test_all.py             |  65 +++++++++
 8 files changed, 451 insertions(+), 38 deletions(-)
 create mode 100755 Tests/FunctionalTests/TestCore/TestCore.py
 rename Tests/FunctionalTests/{TestCore/test_all.py => TestFit/TestFit.py} (86%)
 create mode 100644 Tests/FunctionalTests/TestPyCore/TestPyCore.py
 create mode 100644 Tests/FunctionalTests/TestPyFit/README
 create mode 100644 Tests/FunctionalTests/TestPyFit/TestPyFit.py
 rename Tests/FunctionalTests/{TestPyCore => TestPyFit}/test_all.py (68%)
 create mode 100644 Tests/FunctionalTests/TestPyFit/testfit01.py
 create mode 100644 Tests/FunctionalTests/test_all.py

diff --git a/Tests/FunctionalTests/TestCore/TestCore.py b/Tests/FunctionalTests/TestCore/TestCore.py
new file mode 100755
index 00000000000..248d478a06f
--- /dev/null
+++ b/Tests/FunctionalTests/TestCore/TestCore.py
@@ -0,0 +1,87 @@
+# Run C++ core tests for libBornAgainCore library
+# Usage: python test_all.py
+import sys
+import os
+import subprocess
+import time
+
+
+Tests = [
+  "IsGISAXS01",
+  "IsGISAXS02",
+  "IsGISAXS03",
+  "IsGISAXS04",
+  "IsGISAXS06",
+  "IsGISAXS07",
+  "IsGISAXS08",
+  "IsGISAXS09",
+  "IsGISAXS10",
+  "IsGISAXS11",
+  "IsGISAXS15"
+]
+
+test_info = []
+
+# run system command and catch multiline stdout and stderr
+def run_command(command):
+  p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  p.wait()
+  return iter(p.stdout.readline, b''), iter(p.stderr.readline, b'')
+
+
+# parse stdout, stderr for test description and test result
+def parse_output(testName, stdout, stderr):
+  # normally the message from test looks like "IsGISAXS01 Mixture of cylinders and prisms [OK]"
+  # we want to find status (FAILED or OK) and extract description "Mixture of cylinders and prisms"
+  status="OK"
+  for line in stderr:
+    status="FAILED" # test failed, if there are some non empty stderr messages
+  descr=""
+  for line in stdout:
+    if testName in line:
+      if "FAILED" in line: status="FAILED"
+      descr=line.strip(testName).strip("\n")
+      descr=descr.strip("[OK]")
+      descr=descr.strip("[FAILED]")
+
+  descr = descr[:55]
+  descr = descr.ljust(55).lstrip()
+
+  return descr, status
+
+
+# run tests one by one
+def runTests():
+  print ">>> Starting TestCore"
+  for testName in Tests:
+    command =  testName+"/"+testName # i.e. "path/executable" like "IsGISAXS01/IsGISAXS01"
+    path = os.path.split(__file__)[0]
+    if path: command = path + "/" + command
+    print "Running test ", testName
+    start_time = time.time()
+    stdout, stderr = run_command(command)
+    total_time = time.time() - start_time
+    descr, status = parse_output(testName, stdout, stderr)
+    test_info.append((testName, descr, total_time, status))
+  return getSummary()
+
+
+# making summary
+def getSummary():
+  summary  = "--------------------------------------------------------------------------------\n"
+  summary += "Functional Tests of libBornAgainCore (C++)                                      \n"
+  summary += "--------------------------------------------------------------------------------\n"
+  n=1
+  for x in test_info:
+    summary += '{0:2d}. {1} {2:.53s}  {3:.2f}s [{4}] '.format(n, x[0],x[1],x[2],x[3]) + "\n"
+    n+=1
+  return summary
+
+
+#-------------------------------------------------------------
+# main()
+#-------------------------------------------------------------
+if __name__ == '__main__':
+  summary = runTests()
+  print summary
+
diff --git a/Tests/FunctionalTests/TestCore/test_all.py b/Tests/FunctionalTests/TestFit/TestFit.py
similarity index 86%
rename from Tests/FunctionalTests/TestCore/test_all.py
rename to Tests/FunctionalTests/TestFit/TestFit.py
index 415cbe856cf..8d81ba91aa5 100755
--- a/Tests/FunctionalTests/TestCore/test_all.py
+++ b/Tests/FunctionalTests/TestFit/TestFit.py
@@ -1,23 +1,13 @@
-# Run C++ core tests for libBornAgainCore library
+# Run C++ fitting tests for libBornAgainFit library
 # Usage: python test_all.py
+
 import sys
 import os
 import subprocess
 import time
 
-
 Tests = [
-    "IsGISAXS01",
-    "IsGISAXS02",
-    "IsGISAXS03",
-    "IsGISAXS04",
-    "IsGISAXS06",
-    "IsGISAXS07",
-    "IsGISAXS08",
-    "IsGISAXS09",
-    "IsGISAXS10",
-    "IsGISAXS11",
-    "IsGISAXS15"
+    "TestFit01",
 ]
 
 test_info = []
@@ -64,7 +54,7 @@ def runTests():
 # print test results
 def printResults():
     print "-------------------------------------------------------------------------------"
-    print "TestCore Summary                                                                "
+    print "TestFit Summary                                                                "
     print "-------------------------------------------------------------------------------"
     n=1
     for x in test_info:
diff --git a/Tests/FunctionalTests/TestPyCore/TestPyCore.py b/Tests/FunctionalTests/TestPyCore/TestPyCore.py
new file mode 100644
index 00000000000..388113aa6e0
--- /dev/null
+++ b/Tests/FunctionalTests/TestPyCore/TestPyCore.py
@@ -0,0 +1,83 @@
+# Run Python core tests for libBornAgainCore library
+# Usage: python test_all.py
+
+import sys
+import os
+import subprocess
+import time
+
+import isgisaxs01
+import isgisaxs02
+import isgisaxs03
+import isgisaxs04
+import isgisaxs06
+import isgisaxs07
+import isgisaxs08
+import isgisaxs09
+import isgisaxs10
+import isgisaxs11
+import isgisaxs15
+
+Tests = {
+  "IsGISAXS01": isgisaxs01.runTest,
+  "IsGISAXS02": isgisaxs02.runTest,
+  "IsGISAXS03": isgisaxs03.runTest,
+  "IsGISAXS04": isgisaxs04.runTest,
+  "IsGISAXS06": isgisaxs06.runTest,
+  "IsGISAXS07": isgisaxs07.runTest,
+  "IsGISAXS08": isgisaxs08.runTest,
+  "IsGISAXS09": isgisaxs09.runTest,
+  "IsGISAXS10": isgisaxs10.runTest,
+  "IsGISAXS11": isgisaxs11.runTest,
+  "IsGISAXS15": isgisaxs15.runTest
+}
+
+test_info = []
+
+# parse stdout, stderr for test description and test result
+def parse_output(testName, test_result):
+  # normally the message from test looks like "IsGISAXS01 Mixture of cylinders and prisms [OK]"
+  # we want to find status (FAILED or OK) and extract description "Mixture of cylinders and prisms"
+  status="OK"
+  descr=""
+  if testName in test_result:
+    descr = test_result[1]
+    status = test_result[2]
+  else:
+    descr = "Can't parse the description"
+  descr = descr[:55]
+  descr = descr.ljust(55)
+  return descr, status
+
+
+# run tests one by one
+def runTests():
+  print ">>> Starting TestPyCore"
+  for testName in sorted(Tests.iterkeys()):
+    print "Running test ", testName
+    start_time = time.time()
+    result = Tests[testName]()
+    total_time = time.time() - start_time
+    descr, status = parse_output(testName, result)
+    test_info.append((testName, descr, total_time, status))
+  return getSummary()
+
+
+# compose summary
+def getSummary():
+  summary  = "--------------------------------------------------------------------------------\n"
+  summary += "Functional Tests of libBornAgainCore (Python)                                   \n"
+  summary += "--------------------------------------------------------------------------------\n"
+  n=1
+  for x in test_info:
+    summary += '{0:2d}. {1} {2:.53s}  {3:.2f}s [{4}] '.format(n, x[0],x[1],x[2],x[3]) + "\n"
+    n+=1
+  return summary
+
+
+#-------------------------------------------------------------
+# main()
+#-------------------------------------------------------------
+if __name__ == '__main__':
+  summary = runTests()
+  print summary
diff --git a/Tests/FunctionalTests/TestPyFit/README b/Tests/FunctionalTests/TestPyFit/README
new file mode 100644
index 00000000000..150ef386dcd
--- /dev/null
+++ b/Tests/FunctionalTests/TestPyFit/README
@@ -0,0 +1,12 @@
+Collection of functional tests (Python)
+
+Collection of fitting tests for libBornAgainFit library.
+Different geometries, number of fit parameters, variety of minimizers
+and minimization strategies.
+
+To run tests
+python test_all.py
+
+List of tests
+testfit01.py - Two parameter fit using variety of minimizers. Geometry: cylinders in the air.
+
diff --git a/Tests/FunctionalTests/TestPyFit/TestPyFit.py b/Tests/FunctionalTests/TestPyFit/TestPyFit.py
new file mode 100644
index 00000000000..33c891b3897
--- /dev/null
+++ b/Tests/FunctionalTests/TestPyFit/TestPyFit.py
@@ -0,0 +1,60 @@
+# Run Python fitting tests for libBornAgainFit library
+# Usage: python test_all.py
+
+import sys
+import os
+import subprocess
+import time
+
+import testfit01
+
+Tests = {
+    "TestFit01": testfit01.runTest
+}
+
+test_info = []
+
+# parse stdout, stderr for test description and test result
+def parse_output(testName, test_result):
+    # normally the message from test looks like "IsGISAXS01 Mixture of cylinders and prisms [OK]"
+    # we want to find status (FAILED or OK) and extract description "Mixture of cylinders and prisms"
+    status="OK"
+    descr=""
+    if testName in test_result:
+        descr = test_result[1]
+        status = test_result[2]
+    else:
+       descr = "Can't parse the description"
+    descr = descr[:55]
+    descr = descr.ljust(55)
+    return descr, status
+
+
+# run tests one by one
+def runTests():
+    for testName in sorted(Tests.iterkeys()):
+        print "Running test ", testName
+        start_time = time.time()
+        result = Tests[testName]()
+        total_time = time.time() - start_time
+        descr, status = parse_output(testName, result)
+        test_info.append((testName, descr, total_time, status))
+
+
+# print test results
+def printResults():
+    print "-------------------------------------------------------------------------------"
+    print "TestPyFit Summary                                                              "
+    print "-------------------------------------------------------------------------------"
+    n=1
+    for x in test_info:
+        print '{0:2d}. {1}  {2}  {3:.3f}sec  [{4}] '.format(n, x[0],x[1],x[2],x[3])
+        n+=1
+
+
+#-------------------------------------------------------------
+# main()
+#-------------------------------------------------------------
+if __name__ == '__main__':
+    runTests()
+    printResults()
diff --git a/Tests/FunctionalTests/TestPyCore/test_all.py b/Tests/FunctionalTests/TestPyFit/test_all.py
similarity index 68%
rename from Tests/FunctionalTests/TestPyCore/test_all.py
rename to Tests/FunctionalTests/TestPyFit/test_all.py
index b796aa9f4e4..33c891b3897 100644
--- a/Tests/FunctionalTests/TestPyCore/test_all.py
+++ b/Tests/FunctionalTests/TestPyFit/test_all.py
@@ -1,4 +1,4 @@
-# Run Python core tests for libBornAgainCore library
+# Run Python fitting tests for libBornAgainFit library
 # Usage: python test_all.py
 
 import sys
@@ -6,30 +6,10 @@ import os
 import subprocess
 import time
 
-import isgisaxs01
-import isgisaxs02
-import isgisaxs03
-import isgisaxs04
-import isgisaxs06
-import isgisaxs07
-import isgisaxs08
-import isgisaxs09
-import isgisaxs10
-import isgisaxs11
-import isgisaxs15
+import testfit01
 
 Tests = {
-    "IsGISAXS01": isgisaxs01.runTest,
-    "IsGISAXS02": isgisaxs02.runTest,
-    "IsGISAXS03": isgisaxs03.runTest,
-    "IsGISAXS04": isgisaxs04.runTest,
-    "IsGISAXS06": isgisaxs06.runTest,
-    "IsGISAXS07": isgisaxs07.runTest,
-    "IsGISAXS08": isgisaxs08.runTest,
-    "IsGISAXS09": isgisaxs09.runTest,
-    "IsGISAXS10": isgisaxs10.runTest,
-    "IsGISAXS11": isgisaxs11.runTest,
-    "IsGISAXS15": isgisaxs15.runTest
+    "TestFit01": testfit01.runTest
 }
 
 test_info = []
@@ -64,7 +44,7 @@ def runTests():
 # print test results
 def printResults():
     print "-------------------------------------------------------------------------------"
-    print "TestPyCore Summary                                                             "
+    print "TestPyFit Summary                                                              "
     print "-------------------------------------------------------------------------------"
     n=1
     for x in test_info:
diff --git a/Tests/FunctionalTests/TestPyFit/testfit01.py b/Tests/FunctionalTests/TestPyFit/testfit01.py
new file mode 100644
index 00000000000..6f6e0db94d2
--- /dev/null
+++ b/Tests/FunctionalTests/TestPyFit/testfit01.py
@@ -0,0 +1,136 @@
+# functional test: two parameter fit using variety of minimizers
+#
+# In this test we are using simple geometry: cylinders without interference in
+# air layer with two parameters (radius and height of cylinders), describing
+# the sample. Our "real" data is 2D intensity map obtained from the simulation of
+# the same geometry with fixed values height = 5nm and radius = 5nm.
+# Then we run our minimization consequently using different minimization engines,
+# with height=4nm, radius=6nm as starting fit parameter values.
+
+
+import sys
+import os
+import numpy
+import time
+
+sys.path.append(os.path.abspath(
+                os.path.join(os.path.split(__file__)[0],
+                '..', '..', '..', 'lib')))
+
+from libBornAgainCore import *
+from libBornAgainFit import *
+
+# sample parameters we are going to find
+cylinder_height = 5*nanometer
+cylinder_radius = 5*nanometer
+
+# minimizer name and type of minimization algorithm
+Minimizers = [ 
+    ("Minuit2","Migrad"), 
+    ("Minuit2","Fumili"), 
+    ("GSLMultiMin","BFGS"),
+    ("GSLMultiMin","SteepestDescent"),
+    ("GSLMultiFit",""),
+    ("GSLSimAn","")
+]
+
+
+# -----------------------------------------------------------------------------
+# run several minimization rounds using different minimizers
+# -----------------------------------------------------------------------------
+def runTest():
+  print "**********************************************************************"
+  print "*  Starting  TestFit01                                               *"
+  print "**********************************************************************"
+  nTest=0
+  status = "OK"
+  for m in Minimizers:
+    minimizer_name = m[0]
+    minimizer_algorithm = m[1]
+    print "Test {0:2d}   {1:}({2:})".format(nTest, minimizer_name, minimizer_algorithm)
+    result_ok = run_fitting(minimizer_name, minimizer_algorithm)
+    nTest+=1
+    if not result_ok: status = "FAILED"
+
+  return "TestFit01", "Two parameters fit using variety of minimizers.", status
+
+
+# -----------------------------------------------------------------------------
+# run fitting specified minimizer
+# -----------------------------------------------------------------------------
+def run_fitting(minimizer_name, minimizer_algorithm):
+  sample = buildSample()
+  simulation = createSimulation()
+  simulation.setSample(sample)
+
+  # creating real data, which is simply results of our simulation with default values
+  simulation.runSimulation()
+  real_data = simulation.getOutputDataClone()
+
+  # setting fit suite
+  fitSuite = FitSuite()
+  fitSuite.setMinimizer( MinimizerFactory.createMinimizer(minimizer_name, minimizer_algorithm) )
+  fitSuite.addFitParameter("*height", 4.*nanometer, 0.04*nanometer, AttLimits.lowerLimited(0.01) )
+  fitSuite.addFitParameter("*radius", 6.*nanometer, 0.06*nanometer, AttLimits.lowerLimited(0.01) )
+  fitSuite.addSimulationAndRealData(simulation, real_data)
+
+  # run fit
+  start_time = time.time()
+  fitSuite.runFit()
+  real_time = time.time() - start_time
+
+  height_found = fitSuite.getMinimizer().getValueOfVariableAtMinimum(0)
+  height_diff = abs(height_found - cylinder_height)/cylinder_height
+  radius_found = fitSuite.getMinimizer().getValueOfVariableAtMinimum(1)
+  radius_diff = abs(radius_found - cylinder_radius)/cylinder_radius
+
+  print "          RealTime : {0:.3f} sec".format(real_time)
+  print "          NCalls   : {0:<5d}".format(fitSuite.getNCalls())
+  print '          par1     : {0:.4f} ({1:.3g}) '.format(height_found, height_diff)
+  print '          par2     : {0:.4f} ({1:.3g}) '.format(radius_found, radius_diff)
+
+  diff = 1.0e-02
+  isSuccess = True
+  if( (height_diff > diff) or (radius_diff > diff) ) : isSuccess=False
+  return isSuccess
+
+
+
+# -----------------------------------------------------------------------------
+# create cylinders in the air
+# -----------------------------------------------------------------------------
+def buildSample():
+    cylinder_ff = FormFactorCylinder(cylinder_height, cylinder_radius)
+    n_particle = complex(1.0-6e-4, 2e-8)
+    cylinder = Particle(n_particle, cylinder_ff)
+    interference = InterferenceFunctionNone()
+
+    particle_decoration = ParticleDecoration()
+    particle_decoration.addParticle(cylinder)
+    particle_decoration.addInterferenceFunction(interference)
+
+    mAmbience = MaterialManager.getHomogeneousMaterial("Air", 1.0, 0.0 )
+    air_layer = Layer(mAmbience)
+    air_layer_decorator = LayerDecorator(air_layer, particle_decoration)
+    multi_layer = MultiLayer()
+    multi_layer.addLayer(air_layer_decorator)
+
+    return multi_layer
+
+
+def createSimulation():
+    simulation = Simulation();
+    simulation.setDetectorParameters(100, 0.0*degree, 2.0*degree,100 , 0.0*degree, 2.0*degree);
+    simulation.setBeamParameters(1.0*angstrom, -0.2*degree, 0.0*degree);
+    simulation.setBeamIntensity(1e10);
+    return simulation
+
+
+#-------------------------------------------------------------
+# main()
+#-------------------------------------------------------------
+if __name__ == '__main__':
+  name,description,status = runTest()
+  print name,description,status
+
+
diff --git a/Tests/FunctionalTests/test_all.py b/Tests/FunctionalTests/test_all.py
new file mode 100644
index 00000000000..be9cd09c0df
--- /dev/null
+++ b/Tests/FunctionalTests/test_all.py
@@ -0,0 +1,65 @@
+# run C++/Python functional tests for BornAgain libraries
+#
+# Usage:
+# 'python test_all.py' - to run all tests
+# 'python test_all.py C++' - to run C++ tests only
+# 'python test_all.py Python' - to run Python tests only
+
+import os
+import sys
+import glob
+
+
+sys.path.insert(0, './TestPyCore')
+import TestPyCore
+sys.path.insert(0, './TestCore')
+import TestCore
+
+
+#-------------------------------------------------------------
+# run python functional tests
+#-------------------------------------------------------------
+def runPythonTests():
+  summary = TestPyCore.runTests()
+  return summary
+
+
+#-------------------------------------------------------------
+# run C++ functional tests
+#-------------------------------------------------------------
+def runCppTests():
+  summary = TestCore.runTests()
+  return summary
+
+
+
+#-------------------------------------------------------------
+# main()
+#-------------------------------------------------------------
+def main():
+  run_python_tests = True
+  run_cpp_tests = True
+  if len(sys.argv) >2:
+    print "Usage:"
+    print "'python test_all.py' - to run all tests"
+    print "'python test_all.py C++' - to run C++ tests only"
+    print "'python test_all.py Python' - to run Python tests only"
+    exit()
+  elif len(sys.argv) == 2:
+    if "C++" in sys.argv[1]: run_python_tests = False
+    if "Python" in sys.argv[1]: run_cpp_tests = False
+  print run_python_tests, run_cpp_tests
+
+  summary  = "\n"
+  summary += "Functional Tests Summary -->\n"
+  if run_python_tests: summary += runPythonTests()
+  if run_cpp_tests: summary += runCppTests()
+  print summary
+
+
+#-------------------------------------------------------------
+# main()
+#-------------------------------------------------------------
+if __name__ == '__main__':
+  main()
+
-- 
GitLab