]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.testing/src/org/simantics/db/testing/base/SCLScriptTestBase.java
Report non-existent .sts tests using error logger instead of NPE crash
[simantics/platform.git] / bundles / org.simantics.db.testing / src / org / simantics / db / testing / base / SCLScriptTestBase.java
1 package org.simantics.db.testing.base;
2
3 import java.io.IOException;
4 import java.lang.management.ManagementFactory;
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Timer;
9 import java.util.TimerTask;
10
11 import org.junit.Rule;
12 import org.junit.rules.TestName;
13 import org.osgi.framework.BundleContext;
14 import org.simantics.db.testing.cases.FreshDatabaseTest;
15 import org.simantics.scl.compiler.module.repository.ModuleRepository;
16 import org.simantics.scl.compiler.testing.TestRunnable;
17 import org.simantics.scl.compiler.testing.repository.TestRepository;
18 import org.simantics.scl.osgi.SCLOsgi;
19 import org.simantics.scl.osgi.internal.Activator;
20 import org.simantics.scl.osgi.internal.ServiceBasedModuleSourceRepository;
21 import org.simantics.scl.osgi.internal.ServiceBasedTestRepository;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 import gnu.trove.map.hash.THashMap;
26
27 /**
28  * Utilizies {@link TestRepository} for collecting SCL tests from bundles
29  * 
30  * @author Jani Simomaa
31  */
32 public class SCLScriptTestBase extends FreshDatabaseTest {
33
34     private static final Logger LOGGER = LoggerFactory.getLogger(SCLScriptTestBase.class);
35
36     private Map<String, TestRunnable> testRunnables = new THashMap<String, TestRunnable>();
37
38     @Rule public TestName testName = new TestName();
39
40     /**
41      * Constructor that initially searches for all SCL test scripts and stores
42      * them into a Map for later access
43      */
44     public SCLScriptTestBase() {
45         super();
46         BundleContext context = Activator.getContext();
47         List<TestRunnable> runnables = new ArrayList<TestRunnable>();
48         context.getService(context.getServiceReference(TestRepository.class)).collectTests(runnables);
49         for (TestRunnable runnable : runnables) {
50             testRunnables.put(runnable.getName(), runnable);
51         }
52     }
53
54     /**
55      * Simplest method for running a SCL test
56      */
57     protected void test() {
58         test(-1);
59     }
60
61     /**
62      * Executes a test case with given timeout as seconds. When time runs out one
63      * can assume a deadlock has happened. The process is killed after the timeout
64      * in order to keep the possible multiple tests running.
65      * 
66      * @param timeout allowed execution time given in seconds
67      */
68     protected void test(int timeout) {
69         testImpl(timeout);
70     }
71
72     /**
73      * Executes a test case with given timeout as seconds
74      * 
75      * @param timeout allowed execution time given in seconds
76      */
77     private void testImpl(int timeout) {
78         SCLOsgi.SOURCE_REPOSITORY = new ServiceBasedModuleSourceRepository(Activator.getContext());
79         SCLOsgi.MODULE_REPOSITORY = new ModuleRepository(SCLOsgi.SOURCE_REPOSITORY);
80         SCLOsgi.TEST_REPOSITORY = new ServiceBasedTestRepository(Activator.getContext());
81
82         String testName = resolveTestName();
83         TestRunnable runnable = testRunnables.get(testName);
84         if (runnable == null) {
85             LOGGER.error("SCL Test Suite file (.sts) for test {} cannot be found from test repository.", testName);
86             return;
87         }
88
89         long start = System.nanoTime(); 
90         if (timeout > -1) {
91             LOGGER.info("Running test {} with a timeout of {} seconds", testName, timeout); //$NON-NLS-1$
92             Timer timer = new Timer();
93             timer.schedule(new TimerTask() {
94
95                 @Override
96                 public void run() {
97                     LOGGER.info("Watchdog will kill this test process because it has been executing for over {} seconds", timeout); //$NON-NLS-1$
98                     String processName = ManagementFactory.getRuntimeMXBean().getName();
99                     LOGGER.info("Test Process Name: {}", processName); //$NON-NLS-1$
100                     String PID = processName.split("@")[0];
101                     String command = "taskkill /F /PID " + PID;
102                     LOGGER.info("Running command to kill test process: {}", command); //$NON-NLS-1$
103                     try {
104                         Runtime.getRuntime().exec(command);
105                     } catch (IOException e) {
106                         LOGGER.error("Failed to kill process that ran over its execution time limit of {} seconds", timeout, e);
107                     }
108                 }
109                 
110             }, timeout*1000);
111             try {
112                 runnable.run();
113                 long end = System.nanoTime();
114                 LOGGER.info("Completed test {} execution in {} seconds", testName, String.format("%.3f", (end-start)*1e-9)); //$NON-NLS-1$
115             } catch (Exception e) {
116                 LOGGER.error("Failed to run test {} runnable {}", testName, runnable, e);
117             } finally {
118                 timer.cancel();
119             }
120         } else {
121             LOGGER.info("Running test {} without timeout", testName); //$NON-NLS-1$
122             try {
123                 runnable.run();
124                 long end = System.nanoTime();
125                 LOGGER.info("Completed test {} execution in {} seconds", testName, String.format("%.3f", (end-start)*1e-9)); //$NON-NLS-1$
126             } catch (Exception e) {
127                 LOGGER.error("Failed to run test {} runnable {}", testName, runnable, e);
128             }
129         }
130     }
131
132     /**
133      * Resolves the full test name based on the names of classes that extends this
134      * SCLScriptTestBase class.<br><br>
135      * 
136      * For example if our tests locate in <code>sclTests/Simantics/Regression/FirstTest.sts</code>
137      * the class hierarchy would be:
138      * 
139      * <ul>
140      *   <li><code>SCLScriptTestBase</code>
141      *     <ul>
142      *       <li><code>Simantics</code>
143      *         <ul>
144      *           <li><code>Regression</code></li>
145      *         </ul>
146      *       </li>
147      *     </ul>
148      *   </li>
149      * </ul>
150      * And the script file name is the same than JUnit method name.
151      * 
152      * @return full testName
153      */
154     private String resolveTestName() {
155         StringBuilder sb = new StringBuilder();
156         Class<?> clazz = this.getClass();
157         while (true) {
158             if (!(clazz.getName() == SCLScriptTestBase.class.getName())) {
159                 String[] classNameParts = clazz.getName().split("\\.");
160                 sb.insert(0, "/");
161                 sb.insert(0, classNameParts[classNameParts.length - 1]);
162                 clazz = clazz.getSuperclass();
163             } else {
164                 sb.append(testName.getMethodName());
165                 break;
166             }
167         }
168         return sb.toString();
169     }
170
171 }