@@ -336,13 +336,13 @@ public Stream<TestContext> populate() {
336336 ))
337337 .flatMap (context -> Stream .of (context , context .shift ()))
338338 // 2 clicks is enough to ensure we always fail
339- .map (context -> context .expectInputs (List .of (List .of (), List .of ())));
339+ .map (context -> context .clicks ( 2 ). expectInputs (List .of (List .of (), List .of ())));
340340
341341 final Stream <TestContext > toMatchSingleClick = baseInputs .stream ()
342342 .map (context -> context .name ("Total inventory" ).inventory (totalInitialInventory ))
343343 .flatMap (context -> Stream .of (
344- context .expectInput ( RecipePlaceTest .createExpectedInput (this .expectedInput , 1 )),
345- context .shift ().expectInput ( expectedShiftInput )
344+ context .expectInputAt ( 0 , RecipePlaceTest .createExpectedInput (this .expectedInput , 1 )),
345+ context .shift ().expectInputAt ( 0 , expectedShiftInput )
346346 ));
347347
348348 // After first click we end up with the same layout no matter the initial input.
@@ -353,56 +353,82 @@ public Stream<TestContext> populate() {
353353 .name ("Partial inventory" ).inventory (this .partialInventory ))
354354 .flatMap (context -> Stream .of (
355355 context
356- .expectInputs (IntStream .rangeClosed (1 , this .expectedRegularCrafts )
357- .mapToObj (clicks -> RecipePlaceTest .createExpectedInput (this .expectedInput , clicks ))
358- .toList ())
359- .expectInput (RecipePlaceTest .createExpectedInput (this .expectedInput , this .expectedRegularCrafts )),
360- context .shift ().expectInputs (List .of (expectedShiftInput , expectedShiftInput ))
356+ .clicks (this .expectedRegularCrafts +1 )
357+ .expectInputs (Stream .concat (
358+ IntStream .rangeClosed (1 , this .expectedRegularCrafts )
359+ .mapToObj (clicks -> RecipePlaceTest .createExpectedInput (this .expectedInput , clicks )),
360+ Stream .of (RecipePlaceTest .createExpectedInput (this .expectedInput , this .expectedRegularCrafts ))
361+ ).toList ()),
362+ context .shift ().clicks (2 ).expectInputs (List .of (expectedShiftInput , expectedShiftInput ))
361363 ));
362364
363365 return Stream .concat (toFail , Stream .concat (toMatchSingleClick , toMatchMultipleClicks ));
364366 }
365367 }
366368
367369 private record TestContext (
368- RecipeHolder <?> recipe , String testName , boolean shiftClick ,
370+ RecipeHolder <?> recipe , String testName ,
371+ boolean shiftClick , int clicks ,
369372 List <ItemStack > inventory , List <ItemStack > input ,
370- List <List < ItemStack >> expectedInputs
373+ List <TestEntry > tests
371374 ) {
372375 public TestContext (final RecipeHolder <?> recipe ) {
373- this (recipe , "" , false , List .of (), List .of (), List .of ());
376+ this (recipe , "" , false , 1 , List .of (), List .of (), List .of ());
374377 }
375378
376379 public TestContext name (final String testName ) {
377380 final String newTestName = this .testName .isEmpty () ? testName : (this .testName + ", " + testName );
378- return new TestContext (this .recipe , newTestName , this .shiftClick , this .inventory , this .input , this .expectedInputs );
381+ return new TestContext (this .recipe , newTestName , this .shiftClick , this .clicks , this . inventory , this .input , this .tests );
379382 }
380383
381384 public TestContext shift () {
382- return new TestContext (this .recipe , this .testName , true , this .inventory , this .input , this .expectedInputs );
385+ return new TestContext (this .recipe , this .testName , true , this .clicks , this .inventory , this .input , this .tests );
386+ }
387+
388+ public TestContext clicks (final int clicks ) {
389+ return new TestContext (this .recipe , this .testName , this .shiftClick , clicks , this .inventory , this .input , this .tests );
383390 }
384391
385392 public TestContext inventory (final List <ItemStack > items ) {
386- return new TestContext (this .recipe , this .testName , this .shiftClick , items , this .input , this .expectedInputs );
393+ return new TestContext (this .recipe , this .testName , this .shiftClick , this . clicks , items , this .input , this .tests );
387394 }
388395
389396 public TestContext input (final List <ItemStack > items ) {
390- return new TestContext (this .recipe , this .testName , this .shiftClick , this .inventory , items , this .expectedInputs );
397+ return new TestContext (this .recipe , this .testName , this .shiftClick , this .clicks , this . inventory , items , this .tests );
391398 }
392399
393- public TestContext expectInputs (final List <List <ItemStack >> expectedInputs ) {
394- return new TestContext (this .recipe , this .testName , this .shiftClick , this .inventory , this .input , expectedInputs );
400+ public TestContext test (final TestEntry test ) {
401+ final List <TestEntry > newTests = Stream .concat (this .tests .stream (), Stream .of (test )).toList ();
402+ return new TestContext (this .recipe , this .testName , this .shiftClick , this .clicks , this .inventory , this .input , newTests );
403+ }
404+
405+ public TestContext testAt (final int clickToTest , final TestEntry test ) {
406+ return this .test ((context , input , click ) -> {
407+ if (click != clickToTest ) {
408+ return true ;
409+ }
410+
411+ return test .test (context , input , click );
412+ });
395413 }
396414
397- public TestContext expectInput (final List <ItemStack > expectedInput ) {
398- return this .expectInputs (Stream .concat (this .expectedInputs .stream (), Stream .of (expectedInput )).toList ());
415+ public TestContext expectInputAt (final int clickToTest , final List <ItemStack > expectedInput ) {
416+ return this .testAt (clickToTest , TestEntry .matchInput (expectedInput ));
417+ }
418+
419+ public TestContext expectInputs (final List <List <ItemStack >> expectedInputs ) {
420+ TestContext test = this ;
421+ for (int i = 0 ; i < expectedInputs .size (); ++i ) {
422+ test = test .expectInputAt (i , expectedInputs .get (i ));
423+ }
424+ return test ;
399425 }
400426
401427 public String asTestName () {
402428 return String .format ("Place recipe %s (Shift click: %s, Total clicks: %s, %s)" ,
403429 this .recipe .id ().location ().getPath (),
404430 this .shiftClick ,
405- this .expectedInputs . size () ,
431+ this .clicks ,
406432 this .testName );
407433 }
408434
@@ -442,34 +468,54 @@ public void test(
442468 input .set (i , initialInput .get (i ));
443469 }
444470
445- for (int i = 0 ; i < this .expectedInputs (). size (); ++i ) {
471+ for (int i = 0 ; i < this .clicks (); ++i ) {
446472 menu .handlePlacement (this .shiftClick (), true , this .recipe (), player .serverLevel (), player .getInventory ());
473+ for (final TestEntry test : this .tests ()) {
474+ if (!test .test (this , input , i )) {
475+ break ;
476+ }
477+ }
478+ }
479+ }
480+ }
447481
482+ @ FunctionalInterface
483+ private interface TestEntry {
484+
485+ static TestEntry matchInput (final List <ItemStack > expectedInput ) {
486+ return (context , input , click ) -> {
448487 final List <ItemStack > actualInput = input .slots ().stream ().map (Slot ::peek ).toList ();
449- final List <ItemStack > expectedInput = this .expectedInputs ().get (i );
450- final int click = i +1 ;
451488 for (int j = 0 ; j < actualInput .size (); ++j ) {
452489 final ItemStack actualStack = actualInput .get (j );
453490 final ItemStack expectedStack = expectedInput .size () <= j ? ItemStack .empty () : expectedInput .get (j );
454491 assertTrue (net .minecraft .world .item .ItemStack .matches (
455492 ItemStackUtil .toNative (actualStack ), ItemStackUtil .toNative (expectedStack )),
456493 () -> String .format ("""
457- Actual input doesn't match expected input after click %s
494+ Actual input doesn't match expected input after click № %s
458495 Test: %s,
459496 Expected input: %s
460497 Actual input: %s
461498 Initial input: %s
462499 Initial inventory: %s""" ,
463- click ,
464- this .asTestName (),
500+ click + 1 ,
501+ context .asTestName (),
465502 RecipePlaceTest .stacksToString (false , expectedInput ),
466503 RecipePlaceTest .stacksToString (false , actualInput ),
467- RecipePlaceTest .stacksToString (false , initialInput ),
468- RecipePlaceTest .stacksToString (true , initialInventory )
504+ RecipePlaceTest .stacksToString (false , context . input () ),
505+ RecipePlaceTest .stacksToString (true , context . inventory () )
469506 ));
470507 }
471- }
508+
509+ return true ;
510+ };
472511 }
512+
513+ /**
514+ * Performs the test on recipe input after each placement. Clicks start at 0.
515+ *
516+ * @return True if the following tests should be performed within the given click
517+ */
518+ boolean test (TestContext context , Inventory input , int click );
473519 }
474520
475521 private static final class FakePlayer extends ServerPlayer {
0 commit comments