Skip to content

Commit a4241a7

Browse files
committed
v1.1.0 Handle all test suite exceptions. New UnitTest class.
1 parent ec624cc commit a4241a7

File tree

11 files changed

+173
-55
lines changed

11 files changed

+173
-55
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
}
66

77
def mainClass = "no.stide.fling.Main"
8-
version = "1.0.1"
8+
version = "1.1.0"
99

1010
repositories {
1111
flatDir {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package no.stide.fling;
2+
3+
public enum ExitStatus {
4+
EXIT_SUCCESS,
5+
EXIT_FAILURE
6+
}

src/main/java/no/stide/fling/Procedure.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
@FunctionalInterface
44
public interface Procedure {
5-
void invoke();
5+
void invoke() throws Throwable;
66
}

src/main/java/no/stide/fling/TestCase.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public boolean toBe(Object expectation) {
4040
boolean isEqual = expectation == null || actual == null ? actual == expectation : actual.equals(expectation);
4141
if (isEqual && !not || !isEqual && not) {
4242
this.status = TestStatus.PASSED;
43-
TestSuite.totalPassCount++;
4443
return true;
4544
}
4645
this.status = TestStatus.FAILED;
@@ -54,14 +53,12 @@ public boolean toThrow(Class<? extends Throwable> expectation) {
5453
((Procedure)actual).invoke();
5554
if (not) {
5655
this.status = TestStatus.PASSED;
57-
TestSuite.totalPassCount++;
5856
return true;
5957
}
6058
} catch (Throwable e) {
6159
boolean isEqual = expectation.isInstance(e);
6260
if (isEqual && !not || !isEqual && not) {
6361
this.status = TestStatus.PASSED;
64-
TestSuite.totalPassCount++;
6562
return true;
6663
}
6764
}

src/main/java/no/stide/fling/TestInitiator.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ public TestInitiator(TestSuite parent) {
1010
}
1111

1212
public TestExpector it(String description) {
13-
TestSuite.totalTestCount++;
1413
TestCase test = new TestCase(description);
1514
this.parent.addTest(test);
1615
return test;

src/main/java/no/stide/fling/TestRunner.java

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class TestRunner {
2727
private String[] classpaths;
2828
private ArrayList<String> exclude = new ArrayList<>();
2929
private ArrayList<String> include = new ArrayList<>();
30-
private ArrayList<TestSuite> testSuites = new ArrayList<>();
30+
private UnitTest unitTest = new UnitTest();
3131
private TestStatus testStatus = TestStatus.NOT_STARTED;
3232

3333
public TestRunner(String[] classpaths) {
@@ -36,8 +36,6 @@ public TestRunner(String[] classpaths) {
3636

3737
public void run() throws TestFailedException {
3838

39-
TestSuite.resetTestCounters();
40-
4139
PathMatcher[] excludeMatchers = new PathMatcher[exclude.size()];
4240
for (int i = 0; i < this.exclude.size(); i++) {
4341
excludeMatchers[i] = FileSystems.getDefault().getPathMatcher("glob:" + exclude.get(i));
@@ -84,7 +82,7 @@ public void run() throws TestFailedException {
8482
}
8583
}
8684

87-
this.testStatus = TestSuite.hasPassedAllTests() ? TestStatus.PASSED : TestStatus.FAILED;
85+
this.testStatus = unitTest.hasPassedAllTests() ? TestStatus.PASSED : TestStatus.FAILED;
8886

8987
try {
9088
fullTestSummary();
@@ -109,25 +107,17 @@ private void processClass(String classpath, Path filePath) throws ClassNotFoundE
109107
Method[] methods = clazz.getDeclaredMethods();
110108

111109
Object instance = null;
112-
a: for (Method m : methods) {
110+
for (Method m : methods) {
113111
Annotation[] annotations = m.getAnnotations();
114112
for (Annotation annotation : annotations) {
115113
if (annotation.annotationType().equals(TestGroup.class)) {
116114
try {
117115
Type[] paramTypes = m.getGenericParameterTypes();
118-
if (paramTypes.length < 1 || !(paramTypes[0] instanceof Class
119-
&& (Class<?>) paramTypes[0] == TestInitiator.class)) {
120-
System.err.println(chalk.red()
121-
.apply("\n[ERROR] Method " + getMethodLocation(m)
122-
+ " has the wrong signature! Expected one parameter of type '"
123-
+ TestInitiator.class.getCanonicalName() + "'."));
124-
continue a;
125-
}
126116
if (instance == null) {
127117
System.out.println("Running tests in " + chalk.blue().bold().apply(packagePath) + "...\n");
128118
instance = clazz.getConstructor().newInstance();
129119
}
130-
processMethod(packagePath, instance, m, annotation);
120+
processMethod(packagePath, instance, m, annotation, paramTypes);
131121
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
132122
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
133123
e.printStackTrace();
@@ -138,21 +128,40 @@ private void processClass(String classpath, Path filePath) throws ClassNotFoundE
138128
cl.close();
139129
}
140130

141-
private void processMethod(String classPath, Object instance, Method method, Annotation annotation) {
131+
private void processMethod(String classPath, Object instance, Method method, Annotation annotation, Type[] paramTypes) {
142132
String description = ((TestGroup) annotation).description();
143133
final Object self = instance;
144-
TestSuite suite = new TestSuite(classPath, description, (TestInitiator ti) -> {
134+
TestSuite suite = new TestSuite(classPath, description);
135+
suite.setTestFn((TestInitiator ti) -> {
136+
if (paramTypes.length != 1 || paramTypes.length == 1 &&
137+
!(paramTypes[0] instanceof Class && (Class<?>) paramTypes[0] == TestInitiator.class)) {
138+
System.out.println(chalk.red().apply(
139+
"\n[ERROR] Method " + getMethodLocation(method)
140+
+ " has the wrong signature! Expected one parameter of type '"
141+
+ TestInitiator.class.getCanonicalName() + "'.")
142+
);
143+
suite.setExitStatus(ExitStatus.EXIT_FAILURE);
144+
return;
145+
}
145146
try {
146147
method.invoke(self, ti);
147148
} catch (IllegalAccessException e) {
148-
System.err.println(chalk.red().apply("\n[ERROR] Can't access method " + getMethodLocation(method)
149+
System.out.println(chalk.red().apply("\n[ERROR] Can't access method " + getMethodLocation(method)
149150
+ "! Did you forget to make it public?\n"));
150-
System.err.println(chalk.blackBright().apply(Util.getStackTrace(e)));
151-
} catch (IllegalArgumentException | InvocationTargetException e) {
151+
System.out.println(chalk.blackBright().apply(Util.getStackTrace(e)));
152+
suite.setExitStatus(ExitStatus.EXIT_FAILURE);
153+
} catch (InvocationTargetException e) {
154+
System.out.println(chalk.red().apply(
155+
"\n[ERROR] Test method threw an unexpected exception: "
156+
+ getMethodLocation(method)
157+
));
158+
System.out.println(chalk.blackBright().apply(Util.getStackTrace(e.getCause())));
159+
suite.setExitStatus(ExitStatus.EXIT_FAILURE);
160+
} catch (IllegalArgumentException e) {
152161
e.printStackTrace();
153162
}
154163
});
155-
this.testSuites.add(suite);
164+
this.unitTest.addTestSuite(suite);
156165

157166
ByteArrayOutputStream out = Util.pushOutStack();
158167
suite.run();
@@ -168,6 +177,10 @@ public TestStatus getTestStatus() {
168177
return this.testStatus;
169178
}
170179

180+
public UnitTest getUnitTest() {
181+
return unitTest;
182+
}
183+
171184
public void setClassPaths(String[] classpaths) {
172185
this.classpaths = classpaths;
173186
}
@@ -193,10 +206,10 @@ public void fullTestSummary() throws TestNotStartedException {
193206
throw new TestNotStartedException();
194207
}
195208

196-
TestSuite.printFullSummary();
197-
if (!TestSuite.hasPassedAllTests()) {
209+
unitTest.printFullSummary();
210+
if (!unitTest.hasPassedAllTests()) {
198211
System.out.println(chalk.bgCyan().black().bold().apply(" Failed tests: "));
199-
for (TestSuite suite : this.testSuites) {
212+
for (TestSuite suite : this.unitTest.getTestSuites()) {
200213
if (!suite.hasPassedTests()) {
201214
System.out.println(Util.indent(suite.toString(TestStatus.FAILED), 4));
202215
}

src/main/java/no/stide/fling/TestSuite.java

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
import no.stide.jchalk.Chalk;
77

88
public class TestSuite {
9-
protected static int totalTestCount = 0;
10-
protected static int totalPassCount = 0;
119
private final static Chalk chalk = new Chalk();
1210
private int suitePassCount = 0;
1311
private String classPath;
1412
private String description;
1513
private TestFunction testFn;
1614
private ArrayList<TestCase> tests = new ArrayList<>();
15+
private ExitStatus exitStatus = ExitStatus.EXIT_SUCCESS;
1716

1817
public TestSuite(String description, TestFunction fn) {
1918
this.description = description;
@@ -26,6 +25,11 @@ public TestSuite(String classPath, String description, TestFunction fn) {
2625
this.testFn = fn;
2726
}
2827

28+
protected TestSuite(String classPath, String description) {
29+
this.classPath = classPath;
30+
this.description = description;
31+
}
32+
2933
public void run() {
3034
System.out.println(this.description);
3135

@@ -48,11 +52,6 @@ public void run() {
4852
this.printSummary();
4953
}
5054

51-
public static void resetTestCounters() {
52-
TestSuite.totalTestCount = 0;
53-
TestSuite.totalPassCount = 0;
54-
}
55-
5655
public void printSummary() {
5756
String s = this.hasPassedTests() ? chalk.green().apply("✓") : chalk.red().apply("✗");
5857
System.out.println(
@@ -61,25 +60,16 @@ public void printSummary() {
6160
);
6261
}
6362

64-
public static void printFullSummary() {
65-
String s = hasPassedAllTests() ? chalk.green().apply("✓") : chalk.red().apply("✗");
66-
System.out.println(
67-
"\n" + s + " " + chalk.bold().underline().apply(
68-
"Full test summary: Passed " + TestSuite.totalPassCount + " out of " + TestSuite.totalTestCount + " tests."
69-
) + "\n"
70-
);
71-
}
72-
7363
protected void addTest(TestCase test) {
7464
this.tests.add(test);
7565
}
7666

77-
public static int getTotalTestCount() {
78-
return TestSuite.totalTestCount;
67+
protected void setTestFn(TestFunction testFn) {
68+
this.testFn = testFn;
7969
}
8070

81-
public static int getTotalPassCount() {
82-
return TestSuite.totalPassCount;
71+
public int getTestCount() {
72+
return tests.size();
8373
}
8474

8575
public int getTestPassCount() {
@@ -104,16 +94,20 @@ public String getDescription() {
10494
return this.description;
10595
}
10696

107-
public static boolean hasPassedAllTests() {
108-
return TestSuite.totalPassCount == TestSuite.totalTestCount;
109-
}
110-
11197
public String getClassPath() {
11298
return this.classPath;
11399
}
114100

115101
public boolean hasPassedTests() {
116-
return this.suitePassCount == this.tests.size();
102+
return this.exitStatus == ExitStatus.EXIT_SUCCESS && suitePassCount == this.tests.size();
103+
}
104+
105+
public ExitStatus getExitStatus() {
106+
return exitStatus;
107+
}
108+
109+
public void setExitStatus(ExitStatus exitStatus) {
110+
this.exitStatus = exitStatus;
117111
}
118112

119113
@Override
@@ -129,6 +123,9 @@ public String toString() {
129123
public String toString(TestStatus filter) {
130124
String s = chalk.blue().bold().apply(classPath) + "\n"
131125
+ " " + description + "\n";
126+
if (exitStatus != ExitStatus.EXIT_SUCCESS) {
127+
s += Util.indent(chalk.red().apply("Exit code: " + exitStatus.ordinal()), 4) + "\n";
128+
}
132129
for (TestCase test : getTests(filter)) {
133130
s += Util.indent(test.toString(), 4) + "\n";
134131
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package no.stide.fling;
2+
3+
import java.util.ArrayList;
4+
5+
import no.stide.jchalk.Chalk;
6+
7+
public class UnitTest {
8+
9+
private static final Chalk chalk = new Chalk();
10+
private ArrayList<TestSuite> testSuites = new ArrayList<>();
11+
12+
public boolean hasPassedAllTests() {
13+
for (TestSuite suite : testSuites) {
14+
if (!suite.hasPassedTests()) {
15+
return false;
16+
}
17+
}
18+
return true;
19+
}
20+
21+
public void addTestSuite(TestSuite testSuite) {
22+
this.testSuites.add(testSuite);
23+
}
24+
25+
public int getTotalTestCount() {
26+
int total = 0;
27+
for (TestSuite suite : testSuites) {
28+
total += suite.getTestCount();
29+
}
30+
return total;
31+
}
32+
33+
public int getTotalPassCount() {
34+
int total = 0;
35+
for (TestSuite suite : testSuites) {
36+
total += suite.getTestPassCount();
37+
}
38+
return total;
39+
}
40+
41+
public int getTotalNonSuccessExits() {
42+
int total = 0;
43+
for (TestSuite suite : testSuites) {
44+
if (suite.getExitStatus() != ExitStatus.EXIT_SUCCESS) {
45+
total++;
46+
}
47+
}
48+
return total;
49+
}
50+
51+
public void printFullSummary() {
52+
int nonSuccessExits = getTotalNonSuccessExits();
53+
StringBuilder s = new StringBuilder();
54+
s.append("\n");
55+
s.append(hasPassedAllTests() ? chalk.green().apply("✓") : chalk.red().apply("✗"));
56+
s.append(
57+
" " + chalk.bold().underline().apply(
58+
"Full test summary: Passed " + getTotalPassCount() + " out of " + getTotalTestCount() + " tests."
59+
)
60+
);
61+
if (nonSuccessExits > 0) {
62+
s.append(" " + nonSuccessExits + " test suite");
63+
if (nonSuccessExits > 1) {
64+
s.append("s");
65+
}
66+
s.append(" had a non successfull exit.\n");
67+
}
68+
System.out.println(s.toString());
69+
}
70+
71+
public ArrayList<TestSuite> getTestSuites() {
72+
return testSuites;
73+
}
74+
}

src/test/java/no/stide/TestRunnerTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,13 @@ public class TestRunnerTest {
2323
testRunner.run();
2424
});
2525
}
26+
27+
@Test public void testUnexpected() {
28+
TestRunner testRunner = new TestRunner(new String[] { "build/classes/java/test" });
29+
testRunner.setIncludes("**/mock/UnexpectedEx*");
30+
Assertions.assertThrows(TestFailedException.class, () -> {
31+
testRunner.run();
32+
});
33+
Assertions.assertEquals(testRunner.getUnitTest().getTotalNonSuccessExits(), 3);
34+
}
2635
}

src/test/java/no/stide/mock/Throws.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ public class Throws {
77

88
@TestGroup(description = "Test throw assertion")
99
public void throwTest(TestInitiator suite) {
10-
1110
suite.it("should throw RuntimeException").expect(() -> {
1211
throw new RuntimeException();
1312
}).toThrow(Exception.class);

0 commit comments

Comments
 (0)