11package engine ;
22
3- import java .lang .reflect .InvocationTargetException ;
43import java .lang .reflect .Modifier ;
54import java .util .Arrays ;
65import java .util .Optional ;
1312 * Library of various assertions which can be run about the result of a koan execution.
1413 */
1514public class Assertions {
16- private static String resolveParam (KoanResult res , Object p ) {
17- if (p instanceof FormatParam ) {
18- return ((FormatParam )p ).format (res );
15+ private static String resolveTemplateParam (KoanResult res , Object param ) {
16+ if (param instanceof FormatParam ) {
17+ return ((FormatParam )param ).format (res );
1918 }
2019
21- return Optional .ofNullable (p ).map ((v ) -> v .toString ()).orElse ("" );
20+ return Optional .ofNullable (param ).map ((v ) -> v .toString ()).orElse ("" );
2221 }
2322
24- private static String whenCalling (KoanResult res ) throws IllegalAccessException , ClassNotFoundException , InstantiationException , InvocationTargetException {
23+ private static String whenCalling (KoanResult res ) {
2524 if (res .targetMethod .hasParameters ()) {
2625 return String .format (" when calling %s" , res .targetMethod );
2726 }
@@ -35,7 +34,7 @@ public static ResultAssertion assertIf(boolean condition, ResultAssertion inner)
3534 public static ResultAssertion assertNextStdOutLineEquals (Localizable <String > expectedTemplate , Object ... params ) {
3635 return (p , res ) -> {
3736 final var realParams = Arrays .stream (params )
38- .map ((param ) -> Assertions .resolveParam (res , param ))
37+ .map ((param ) -> Assertions .resolveTemplateParam (res , param ))
3938 .toArray ();
4039 final var expected = String .format (expectedTemplate .get (res .locale ), realParams );
4140 final var lineContent = res .nextStdOutLine ();
@@ -245,7 +244,7 @@ public static ResultAssertion assertReturnValueWithRandomEquals(int fromOffset,
245244 private static ResultAssertion assertReturnValueWithRandomEquals (Function <KoanResult , double []> randomNumbersFunc , ResToIntFunction buildExpected ) {
246245 return (p , res ) -> {
247246 var randomNumbers = randomNumbersFunc .apply (res );
248- var formatRandomNumbers = Helpers .formatSequence (randomNumbers , AND . get ( res .locale ) );
247+ var formatRandomNumbers = Helpers .formatSequence (res .locale , randomNumbers );
249248
250249 int expected = buildExpected .apply (res );
251250 if (res .methodReturnValue == null ) {
@@ -270,9 +269,127 @@ private static ResultAssertion assertReturnValueWithRandomEquals(Function<KoanRe
270269 };
271270 }
272271
272+ public static KoanAssertion assertClassIsInstantiable () {
273+ return (p , locale , koan ) -> {
274+ try {
275+ var clasz = koan .koanClass .get (locale ).resolve ();
276+ if (!Helpers .isInstantiable (clasz )) {
277+ p .println (Color .red (EXPECTED_CLASS_TO_BE_INSTANTIABLE ), koan .koanClass .get (locale ).className );
278+ return false ;
279+ }
280+ } catch (ClassNotFoundException cnfe ) {
281+ p .println (Color .red (EXPECTED_TO_FIND_A_CLASS_IN_THE_PACKAGE ), koan .koanClass .get (locale ).simpleClassName , koan .koanClass .get (locale ).packageName );
282+ return false ;
283+ }
284+
285+ return true ;
286+ };
287+ }
288+
289+ public static KoanAssertion assertConstructorIsInvokable () {
290+ return (p , locale , koan ) -> {
291+ var clasz = koan .koanClass .get (locale ).unsafeResolve ();
292+
293+ try {
294+ var constructor = clasz .getConstructor (Type .unsafeResolveTypes (koan .constructorParamTypes ));
295+ if (!Modifier .isPublic (constructor .getModifiers ())) {
296+ p .println (
297+ Color .red (EXPECTED_CONSTRUCTOR_TO_BE_PUBLIC ),
298+ koan .koanClass .get (locale ).simpleClassName
299+ );
300+ }
301+ }
302+ catch (NoSuchMethodException nsme ) {
303+ if (koan .constructorParamTypes .length == 0 ) {
304+ p .println (
305+ Color .red (EXPECTED_TO_FIND_CONSTRUCTOR_NO_PARAMS ),
306+ koan .koanClass .get (locale ).simpleClassName
307+ );
308+ } else if (koan .constructorParamTypes .length == 1 ) {
309+ p .println (
310+ Color .red (EXPECTED_TO_FIND_CONSTRUCTOR_ONE_PARAM ),
311+ koan .koanClass .get (locale ).simpleClassName ,
312+ koan .constructorParamTypes [0 ]
313+ );
314+ } else {
315+ final var expectedParams = Arrays
316+ .stream (koan .constructorParamTypes )
317+ .map (type -> "'" + type + "'" )
318+ .toArray (String []::new );
319+ p .println (
320+ Color .red (EXPECTED_TO_FIND_CONSTRUCTOR_MANY_PARAMS ),
321+ koan .koanClass .get (locale ).simpleClassName ,
322+ Helpers .formatSequence (locale , expectedParams )
323+ );
324+ }
325+ return false ;
326+ }
327+
328+ return true ;
329+ };
330+ }
331+
332+ public static KoanAssertion assertMethodIsInvokable (String methodName , boolean isStatic , Type ... paramTypes ) {
333+ return assertMethodIsInvokable (methodName , isStatic , Type .unsafeResolveTypes (paramTypes ));
334+ }
335+
336+ public static KoanAssertion assertMethodIsInvokable (String methodName , boolean isStatic , Class <?>... methodParamTypes ) {
337+ return (p , locale , koan ) -> {
338+ var clasz = koan .koanClass .get (locale ).unsafeResolve ();
339+
340+ try {
341+ var method = clasz .getMethod (methodName , methodParamTypes );
342+ if (isStatic && !Modifier .isStatic (method .getModifiers ())) {
343+ p .println (Color .red (EXPECTED_METHOD_TO_NOT_BE_STATIC ), methodName , clasz .getName ().replace ("." , "/" ));
344+ return false ;
345+ }
346+ if (!isStatic && Modifier .isStatic (method .getModifiers ())) {
347+ p .println (Color .red (EXPECTED_METHOD_TO_BE_STATIC ), methodName , clasz .getName ().replace ("." , "/" ));
348+ return false ;
349+ }
350+ if (!Modifier .isPublic (method .getModifiers ())) {
351+ p .println (
352+ Color .red (EXPECTED_METHOD_TO_BE_PUBLIC ),
353+ koan .koanClass .get (locale ).simpleClassName
354+ );
355+ }
356+ }
357+ catch (NoSuchMethodException nsme ) {
358+ if (methodParamTypes .length == 0 ) {
359+ p .println (
360+ Color .red (EXPECTED_TO_FIND_MEHOD_NO_PARAMS ),
361+ methodName ,
362+ clasz .getName ().replace ("." , "/" )
363+ );
364+ } else if (methodParamTypes .length == 1 ) {
365+ p .println (
366+ Color .red (EXPECTED_TO_FIND_MEHOD_ONE_PARAM ),
367+ methodName ,
368+ clasz .getName ().replace ("." , "/" ),
369+ methodParamTypes [0 ].getSimpleName ()
370+ );
371+ } else {
372+ final var expectedParams = Arrays
373+ .stream (methodParamTypes )
374+ .map (type -> "'" + type .getSimpleName () + "'" )
375+ .toArray (String []::new );
376+ p .println (
377+ Color .red (EXPECTED_TO_FIND_MEHOD_MANY_PARAMS ),
378+ methodName ,
379+ clasz .getName ().replace ("." , "/" ),
380+ Helpers .formatSequence (locale , expectedParams )
381+ );
382+ }
383+ return false ;
384+ }
385+
386+ return true ;
387+ };
388+ }
389+
273390 public static KoanAssertion assertFieldIsPrivate (String fieldName ) {
274- return (p , methodDetails ) -> {
275- var clasz = methodDetails . clasz ;
391+ return (p , locale , koan ) -> {
392+ var clasz = koan . koanClass . get ( locale ). unsafeResolve () ;
276393
277394 try {
278395 var field = clasz .getDeclaredField (fieldName );
@@ -291,8 +408,8 @@ public static KoanAssertion assertFieldIsPrivate(String fieldName) {
291408 }
292409
293410 public static KoanAssertion assertFieldIsFinal (String fieldName ) {
294- return (p , methodDetails ) -> {
295- var clasz = methodDetails . clasz ;
411+ return (p , locale , koan ) -> {
412+ var clasz = koan . koanClass . get ( locale ). unsafeResolve () ;
296413
297414 try {
298415 var field = clasz .getDeclaredField (fieldName );
@@ -311,12 +428,12 @@ public static KoanAssertion assertFieldIsFinal(String fieldName) {
311428 }
312429
313430 public static KoanAssertion assertFieldType (String fieldName , Type fieldType ) {
314- return (p , methodDetails ) -> {
315- var clasz = methodDetails . clasz ;
431+ return (p , locale , koan ) -> {
432+ var clasz = koan . koanClass . get ( locale ). unsafeResolve () ;
316433
317434 try {
318435 var field = clasz .getDeclaredField (fieldName );
319- if (!field .getType ().equals (fieldType .resolve ())) {
436+ if (!field .getType ().equals (fieldType .unsafeResolve ())) {
320437 p .println (Color .red (EXPECTED_FIELD_TO_BE_OF_TYPE ), fieldName , clasz .getName (), fieldType , field .getType ().getSimpleName ());
321438 return false ;
322439 }
0 commit comments