11package io .github .slimefun .e2etester .framework ;
22
3+ import java .lang .annotation .Annotation ;
4+ import java .lang .reflect .InvocationTargetException ;
35import java .lang .reflect .Method ;
46import java .util .ArrayList ;
57import java .util .HashMap ;
68import java .util .LinkedHashMap ;
79import java .util .List ;
810import java .util .Map ;
911import java .util .Set ;
12+ import java .util .concurrent .CountDownLatch ;
13+ import java .util .concurrent .ExecutorService ;
14+ import java .util .concurrent .Executors ;
15+ import java .util .concurrent .atomic .AtomicBoolean ;
1016import java .util .function .Consumer ;
1117import java .util .stream .Collectors ;
1218
1319import javax .annotation .Nonnull ;
1420
21+ import io .github .slimefun .e2etester .E2ETester ;
1522import org .bukkit .Bukkit ;
1623import org .reflections .Reflections ;
1724import org .reflections .scanners .Scanners ;
2431public class TestFramework {
2532
2633 private final Map <String , ArrayList <Consumer <Void >>> eventListeners = new HashMap <>();
34+ private final ExecutorService service = Executors .newSingleThreadExecutor ();
2735
2836 private int testsRan ;
2937 private int testsPassed ;
@@ -41,63 +49,89 @@ public void runTests(@Nonnull String packageName) {
4149 // TODO: Remove
4250 new Startup ();
4351
44- // Reflection go through all classes
45- Reflections reflections = new Reflections (ConfigurationBuilder
46- .build ()
47- .addScanners (Scanners .values ())
48- .forPackage (packageName , getClass ().getClassLoader ())
49- );
50-
51- logMessage ("Running tests..." );
52- logMessage ("" );
53-
54- final Set <Method > testMethods = reflections .get (Scanners .MethodsAnnotated .with (E2ETest .class ).as (Method .class ));
55- final Map <Class <?>, List <Method >> tests = orderTests (testMethods );
56- for (Map .Entry <Class <?>, List <Method >> entry : tests .entrySet ()) {
57- logMessage ("%s:" , entry .getKey ().getName ());
58-
59- for (Method method : entry .getValue ()) {
60- String description = method .getAnnotation (E2ETest .class ).description ();
61-
62- // Invoke
63- try {
64- testsRan ++;
65-
66- method .setAccessible (true );
67- Object instance = method .getDeclaringClass ().getDeclaredConstructor ().newInstance ();
68- method .invoke (instance );
69-
70- testsPassed ++;
71- logMessage (" ✔ %s" , description );
72- } catch (TestFailException e ) {
73- testsFailed ++;
74- logMessage (" x %s" , description );
75- e .printStackTrace ();
76- } catch (Exception e ) {
77- testsFailed ++;
78- logMessage (" x %s" , description );
79- e .printStackTrace ();
52+ service .submit (() -> {
53+ // Reflection go through all classes
54+ Reflections reflections = new Reflections (ConfigurationBuilder
55+ .build ()
56+ .addScanners (Scanners .values ())
57+ .forPackage (packageName , getClass ().getClassLoader ())
58+ );
59+
60+ logMessage ("Running tests..." );
61+ logMessage ("" );
62+
63+ final Set <Method > testMethods = reflections .get (Scanners .MethodsAnnotated .with (E2ETest .class ).as (Method .class ));
64+ final Map <Class <?>, List <Method >> tests = orderTests (testMethods );
65+ for (Map .Entry <Class <?>, List <Method >> entry : tests .entrySet ()) {
66+ logMessage ("%s:" , entry .getKey ().getName ());
67+
68+ for (Method method : entry .getValue ()) {
69+ E2ETest annotation = method .getAnnotation (E2ETest .class );
70+ String description = annotation .description ();
71+ boolean threadSafe = annotation .threadSafe ();
72+
73+ CountDownLatch latch = new CountDownLatch (1 );
74+ AtomicBoolean failed = new AtomicBoolean (false );
75+
76+ // Invoke
77+ try {
78+ testsRan ++;
79+ method .setAccessible (true );
80+ Object instance = method .getDeclaringClass ().getDeclaredConstructor ().newInstance ();
81+ if (threadSafe ) {
82+ method .invoke (instance );
83+ } else {
84+ Bukkit .getScheduler ().runTask (E2ETester .getInstance (), () -> {
85+ try {
86+ method .invoke (instance );
87+ latch .countDown ();
88+ } catch (IllegalAccessException | InvocationTargetException e ) {
89+ latch .countDown ();
90+ if (e .getCause () instanceof TestFailException ) {
91+ failed .set (true );
92+ e .printStackTrace ();
93+ }
94+ }
95+ });
96+ latch .await ();
97+ }
98+ if (failed .get ()) {
99+ testsFailed ++;
100+ logMessage (" x %s" , description );
101+ } else {
102+ testsPassed ++;
103+ logMessage (" ✔ %s" , description );
104+ }
105+ } catch (TestFailException e ) {
106+ testsFailed ++;
107+ logMessage (" x %s" , description );
108+ e .printStackTrace ();
109+ } catch (Exception e ) {
110+ testsFailed ++;
111+ logMessage (" x %s" , description );
112+ e .printStackTrace ();
113+ }
80114 }
81115 }
82- }
83-
84- logMessage ("" );
85- logMessage ("Test results:" );
86- logMessage (" Tests ran: %d" , this .testsRan );
87- logMessage (" Tests passed: %d" , this .testsPassed );
88- logMessage (" Tests failed: %d" , this .testsFailed );
89- logMessage (" Tests skipped: %d" , this .testsSkipped );
90116
91- if (this .testsFailed > 0 ) {
92- logMessage ("Test failure, exiting with code 1" );
93- System .exit (1 );
94- } else {
95- Bukkit .shutdown ();
96- }
117+ logMessage ("" );
118+ logMessage ("Test results:" );
119+ logMessage (" Tests ran: %d" , this .testsRan );
120+ logMessage (" Tests passed: %d" , this .testsPassed );
121+ logMessage (" Tests failed: %d" , this .testsFailed );
122+ logMessage (" Tests skipped: %d" , this .testsSkipped );
123+
124+ if (this .testsFailed > 0 ) {
125+ logMessage ("Test failure, exiting with code 1" );
126+ System .exit (1 );
127+ } else {
128+ Bukkit .getScheduler ().runTask (E2ETester .getInstance (), Bukkit ::shutdown );
129+ }
130+ });
97131 }
98132
99133 public void on (@ Nonnull String event , Consumer <Void > consumer ) {
100- eventListeners .putIfAbsent (event , new ArrayList <Consumer < Void > >());
134+ eventListeners .putIfAbsent (event , new ArrayList <>());
101135
102136 ArrayList <Consumer <Void >> value = eventListeners .get (event );
103137 if (value != null ) {
@@ -146,7 +180,7 @@ private Map<Class<?>, List<Method>> orderTests(Set<Method> methods) {
146180 return tests .entrySet ().stream ()
147181 .sorted ((entry1 , entry2 ) -> entry1 .getKey ().getName ().compareTo (entry2 .getKey ().getName ()))
148182 .collect (Collectors .toMap (
149- entry -> entry . getKey () ,
183+ Map . Entry :: getKey ,
150184 value -> value .getValue ().stream ()
151185 .sorted ((method1 , method2 ) -> method1 .getName ().compareTo (method2 .getName ()))
152186 .toList ()
0 commit comments