@@ -52,31 +52,62 @@ public static void assertScreenshotEquals(ClientGameTestContext context,
5252 String fileName , String templateUrl )
5353 {
5454 ThreadingImpl .checkOnGametestThread ("assertScreenshotEquals" );
55+ waitForScreenshotMatchImpl (context , fileName , templateUrl , 1 );
56+ }
57+
58+ /**
59+ * Same as
60+ * {@link #assertScreenshotEquals(ClientGameTestContext, String, String)},
61+ * but retries for up to 10 seconds to get a matching screenshot.
62+ *
63+ * <p>
64+ * Useful for cases where you're waiting for recent movements to settle
65+ * (e.g. chunk reloads, hand animation), where it can otherwise be tricky to
66+ * get the timing right. Not useful for anything that's still in motion,
67+ * where delaying the screenshot would only cause it to drift further away
68+ * from the expected image.
69+ */
70+ public static void waitForScreenshotMatch (ClientGameTestContext context ,
71+ String fileName , String templateUrl )
72+ {
73+ ThreadingImpl .checkOnGametestThread ("waitForScreenshotMatch" );
74+ waitForScreenshotMatchImpl (context , fileName , templateUrl ,
75+ ClientGameTestContext .DEFAULT_TIMEOUT );
76+ }
77+
78+ private static void waitForScreenshotMatchImpl (
79+ ClientGameTestContext context , String fileName , String templateUrl ,
80+ int maxAttempts )
81+ {
82+ NativeImage nativeImageTemplate = downloadImage (templateUrl );
83+ boolean [][] mask = alphaChannelToMask (nativeImageTemplate );
84+ RawImage <int []> rawTemplate =
85+ RawImageImpl .fromColorNativeImage (nativeImageTemplate );
86+ RawImage <int []> maskedTemplate = applyMask (rawTemplate , mask );
5587
56- NativeImage nativeTemplateImage = downloadImage (templateUrl );
57- boolean [][] mask = alphaChannelToMask (nativeTemplateImage );
58- RawImage <int []> rawTemplateImage =
59- RawImageImpl .fromColorNativeImage (nativeTemplateImage );
60- RawImage <int []> maskedTemplateImage = applyMask (rawTemplateImage , mask );
61-
62- Path screenshotPath = context .takeScreenshot (fileName );
63- RawImage <int []> rawScreenshotImage =
64- RawImageImpl .fromColorNativeImage (loadImageFile (screenshotPath ));
65- RawImage <int []> maskedScreenshotImage =
66- applyMask (rawScreenshotImage , mask );
67-
68- if (maskedScreenshotImage .width () != maskedTemplateImage .width ()
69- || maskedScreenshotImage .height () != maskedTemplateImage .height ())
70- throw new AssertionError (
71- "Screenshot and template dimensions do not match" );
72-
73- TestScreenshotComparisonAlgorithm algo =
74- TestScreenshotComparisonAlgorithm .meanSquaredDifference (3e-4F );
75-
76- Vector2i result =
77- algo .findColor (maskedScreenshotImage , maskedTemplateImage );
78- if (result != null )
79- return ;
88+ Path screenshotPath = null ;
89+ for (int i = 0 ; i < maxAttempts ; i ++)
90+ {
91+ if (i > 0 )
92+ context .waitTick ();
93+
94+ screenshotPath = context .takeScreenshot (fileName );
95+ RawImage <int []> rawScreenshot = RawImageImpl
96+ .fromColorNativeImage (loadImageFile (screenshotPath ));
97+ RawImage <int []> maskedScreenshot = applyMask (rawScreenshot , mask );
98+
99+ if (maskedScreenshot .width () != maskedTemplate .width ()
100+ || maskedScreenshot .height () != maskedTemplate .height ())
101+ throw new AssertionError (
102+ "Screenshot and template dimensions do not match" );
103+
104+ TestScreenshotComparisonAlgorithm algo =
105+ TestScreenshotComparisonAlgorithm .meanSquaredDifference (3e-4F );
106+
107+ Vector2i result = algo .findColor (maskedScreenshot , maskedTemplate );
108+ if (result != null )
109+ return ;
110+ }
80111
81112 ghSummary ("### Screenshot " + fileName + " does not match template" );
82113 ghSummary ("Expected:" );
0 commit comments