/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.impl.javase;
import com.codename1.testing.TestReporting;
import java.io.DataInputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.StringTokenizer;
/**
* The test runner allows running Codename One unit tests on a specific Codename One application within the
* simulator.
*
* @author Shai Almog
*/
public class TestRunner {
private static final int VERSION = 1;
private String mainClass;
private String[] testCases;
private String[] skins;
private boolean quietMode;
private boolean cleanMode = true;
private boolean stopOnFail;
private TestRunner() {
}
private void printUsage() {
System.out.println("Usage: TestRunner mainClass [-testCases testCase1,testCase2...] [-skins skin1,skin2...] [-quiet] [-cleanMode]"
+ "[-stopOnFail]\n\n"
+ "mainClass - the main application class which is being tested, this is the full name for the lifecycle class.\n"
+ "testCases - optional set of test cases to run using the full package/class name syntax and comma delimited. If "
+ "ommitted all test cases will be executed.\n"
+ "skins - the skins on which the test cases should be executed. If ommitted the default simulator skin is used.\n"
+ "quietMode - when set the skin running the tests will not appear and the tests will be executed in the background\n"
+ "cleanMode - clean mode executes every test in complete isolation from the previous test restarting the Classloader"
+ "completely. Clean mode can't be used on the device so its only useful for debugging\n"
+ "stopOnFail - indicates that execution should stop the moment a failure occured");
}
private void init(String[] argv) {
try {
if(argv[0].startsWith("-") || argv[0].startsWith("/")) {
printUsage();
return;
}
try {
mainClass = argv[0];
int pos = 1;
while(pos < argv.length) {
String s = argv[pos];
if(s.equalsIgnoreCase("-testCases")) {
pos++;
testCases = argv[pos].split(",");
pos++;
continue;
}
if(s.equalsIgnoreCase("-skins")) {
pos++;
skins = argv[pos].split(",");
pos++;
continue;
}
if(s.equalsIgnoreCase("-quietMode")) {
quietMode = true;
pos++;
continue;
}
if(s.equalsIgnoreCase("-cleanMode")) {
cleanMode = true;
pos++;
continue;
}
System.out.println("Unrecognized argument: " + s);
printUsage();
System.exit(1);
return;
}
} catch(Exception err) {
err.printStackTrace();
printUsage();
return;
}
String[] tests;
if(testCases == null || testCases.length == 0) {
InputStream is = getClass().getResourceAsStream("/tests.dat");
if(is == null) {
System.err.println("Test data not found in the file, make sure the ant task was executed in full");
System.exit(2);
return;
}
DataInputStream di = new DataInputStream(is);
int version = di.readInt();
if(version > VERSION) {
System.err.println("Tests were built with a new version of Codename One and can't be executed with this runner");
System.exit(4);
return;
}
tests = new String[di.readInt()];
for(int iter = 0 ; iter < tests.length ; iter++) {
tests[iter] = di.readUTF();
}
di.close();
} else {
tests = testCases;
}
System.out.println("Preparing to execute " + tests.length + " tests");
StringTokenizer t = new StringTokenizer(System.getProperty("java.class.path"), File.pathSeparator);
File[] files = new File[t.countTokens()];
for (int iter = 0; iter < files.length; iter++) {
files[iter] = new File(t.nextToken());
}
int passedTests = 0;
int failedTests = 0;
if(cleanMode) {
for(String currentTestClass : tests) {
ClassLoader ldr = new ClassPathLoader(files);
Class c = Class.forName("com.codename1.impl.javase.TestExecuter", true, ldr);
Method m = c.getDeclaredMethod("runTest", String.class, String.class, Boolean.TYPE);
Boolean passed = (Boolean)m.invoke(null, mainClass, currentTestClass, quietMode);
if(passed.booleanValue()) {
passedTests++;
} else {
failedTests++;
if(stopOnFail) {
System.exit(100);
return;
}
}
}
} else {
ClassLoader ldr = new ClassPathLoader(files);
Class c = Class.forName("com.codename1.impl.javase.TestExecuter", true, ldr);
for(String currentTestClass : tests) {
Method m = c.getDeclaredMethod("runTest", String.class, String.class, Boolean.TYPE);
Boolean passed = (Boolean)m.invoke(null, mainClass, currentTestClass, quietMode);
if(passed.booleanValue()) {
System.out.println(currentTestClass + " passed!");
passedTests++;
} else {
System.out.println(currentTestClass + " failed!");
failedTests++;
if(stopOnFail) {
System.exit(100);
return;
}
}
}
}
TestReporting.getInstance().testExecutionFinished();
if(failedTests > 0) {
System.out.println("Test execution finished, some failed tests occured. Passed: " + passedTests + " tests. Failed: " + failedTests + " tests.");
} else {
System.out.println("All tests passed. Total " + passedTests + " tests passed");
}
System.exit(0);
} catch (Exception ex) {
ex.printStackTrace();
System.exit(3);
}
}
/**
* The main method accepts several arguments of which only the main class is a requirement
*/
public static void main(String[] argv) {
new TestRunner().init(argv);
}
}