From eff082ca764ff4c60d06346df1484b312dddbcf5 Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 5 Sep 2024 16:00:36 -0400 Subject: [PATCH] fall tree tests, testing utils, assertion macros --- .../audio/movement/MovementAudioService.java | 57 ++++----- .../collision/CollisionEngine.java | 9 +- .../collision/collidable/Collidable.java | 3 + .../collidable/ServerCollidableTree.java | 24 ++-- .../state/gravity/ServerGravityTree.java | 4 + .../movement/fall/ClientFallTreeTests.java | 61 ++++++++++ .../movement/fall/ServerFallTreeTests.java | 61 ++++++++++ .../test/template/RenderingTestTemplate.java | 3 +- .../test/testutils/Assertions.java | 110 ++++++++++++++++++ .../test/testutils/TestEngineUtils.java | 54 +++++++++ .../test/testutils/TestRenderingUtils.java | 70 ----------- 11 files changed, 347 insertions(+), 109 deletions(-) create mode 100644 src/test/java/electrosphere/entity/state/movement/fall/ClientFallTreeTests.java create mode 100644 src/test/java/electrosphere/entity/state/movement/fall/ServerFallTreeTests.java create mode 100644 src/test/java/electrosphere/test/testutils/Assertions.java diff --git a/src/main/java/electrosphere/audio/movement/MovementAudioService.java b/src/main/java/electrosphere/audio/movement/MovementAudioService.java index 1b14a3fb..35ee263f 100644 --- a/src/main/java/electrosphere/audio/movement/MovementAudioService.java +++ b/src/main/java/electrosphere/audio/movement/MovementAudioService.java @@ -98,12 +98,15 @@ public class MovementAudioService { * @return The path to the audio file to play */ public String getAudioPath(int voxelType, InteractionType type){ + String rVal = null; SurfaceAudioType surfaceAudio = this.defaultSurfaceAudio; //Check if ignored - for(int ignoredVoxelType : this.ignoredVoxelTypes){ - if(ignoredVoxelType == voxelType){ - return null; + if(this.ignoredVoxelTypes != null){ + for(int ignoredVoxelType : this.ignoredVoxelTypes){ + if(ignoredVoxelType == voxelType){ + return null; + } } } @@ -115,31 +118,33 @@ public class MovementAudioService { } //gets the list to pull from - List availableFiles = surfaceAudio.getFootstepRegularBareAudioPaths(); - switch(type){ - case STEP_BARE_REG: { - availableFiles = surfaceAudio.getFootstepRegularBareAudioPaths(); - } break; - case STEP_BARE_HEAVY: { - availableFiles = surfaceAudio.getFootstepHeavyBareAudioPaths(); - } break; - case STEP_SHOE_REG: { - availableFiles = surfaceAudio.getFootstepRegularShoeAudioPaths(); - } break; - case STEP_SHOE_HEAVY: { - availableFiles = surfaceAudio.getFootstepHeavyShoeAudioPaths(); - } break; - case JUMP: { - availableFiles = surfaceAudio.getJumpAudioPaths(); - } break; - case LAND: { - availableFiles = surfaceAudio.getLandAudioPaths(); - } break; + if(surfaceAudio != null){ + List availableFiles = surfaceAudio.getFootstepRegularBareAudioPaths(); + switch(type){ + case STEP_BARE_REG: { + availableFiles = surfaceAudio.getFootstepRegularBareAudioPaths(); + } break; + case STEP_BARE_HEAVY: { + availableFiles = surfaceAudio.getFootstepHeavyBareAudioPaths(); + } break; + case STEP_SHOE_REG: { + availableFiles = surfaceAudio.getFootstepRegularShoeAudioPaths(); + } break; + case STEP_SHOE_HEAVY: { + availableFiles = surfaceAudio.getFootstepHeavyShoeAudioPaths(); + } break; + case JUMP: { + availableFiles = surfaceAudio.getJumpAudioPaths(); + } break; + case LAND: { + availableFiles = surfaceAudio.getLandAudioPaths(); + } break; + } + int roll = random.nextInt(availableFiles.size()); + rVal = availableFiles.get(roll); } - //return the audio - int roll = random.nextInt(availableFiles.size()); - return availableFiles.get(roll); + return rVal; } /** diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index bf32c78d..2f6a382f 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -466,10 +466,13 @@ public class CollisionEngine { DBody rigidBody = PhysicsEntityUtils.getDBody(physicsEntity); Matrix4d inverseTransform = new Matrix4d(); Vector4d rawPos = inverseTransform.transform(new Vector4d(PhysicsUtils.getRigidBodyPosition(rigidBody),1)); - Vector3d newPosition = new Vector3d(rawPos.x,rawPos.y,rawPos.z); - newPosition = this.suggestMovementPosition(collisionWorldData, newPosition); + Vector3d calculatedPosition = new Vector3d(rawPos.x,rawPos.y,rawPos.z); + Vector3d suggestedPosition = this.suggestMovementPosition(collisionWorldData, calculatedPosition); + if(calculatedPosition.distance(suggestedPosition) > 0){ + collidable.addImpulse(new Impulse(new Vector3d(), new Vector3d(), new Vector3d(), 0, Collidable.TYPE_WORLD_BOUND)); + } Quaterniond newRotation = PhysicsUtils.getRigidBodyRotation(rigidBody); - EntityUtils.getPosition(physicsEntity).set(newPosition); + EntityUtils.getPosition(physicsEntity).set(suggestedPosition); EntityUtils.getRotation(physicsEntity).set(newRotation); } } diff --git a/src/main/java/electrosphere/collision/collidable/Collidable.java b/src/main/java/electrosphere/collision/collidable/Collidable.java index 82e4a163..b86a6524 100644 --- a/src/main/java/electrosphere/collision/collidable/Collidable.java +++ b/src/main/java/electrosphere/collision/collidable/Collidable.java @@ -47,6 +47,9 @@ public class Collidable { public static final String TYPE_FOLIAGE_STATIC = "foliageStatic"; public static final long TYPE_FOLIAGE_BIT = 0x80; + public static final String TYPE_WORLD_BOUND = "worldBound"; + public static final long TYPE_WORLD_BOUND_BIT = 0x100; + /** * Constructor diff --git a/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java b/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java index acd42a20..6f77785d 100644 --- a/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java +++ b/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java @@ -44,29 +44,23 @@ public class ServerCollidableTree implements BehaviorTree { public void simulate(float deltaTime){ Vector3d position = EntityUtils.getPosition(parent); Quaterniond rotation = EntityUtils.getRotation(parent); - Vector3d offsetVector = new Vector3d(); Vector3d newPosition = new Vector3d(position); //have we hit a terrain impulse? - boolean hitTerrain = false; //handle impulses for(Impulse impulse : collidable.getImpulses()){ -// collidable.getImpulses().remove(impulse); - if(impulse.type.matches(Collidable.TYPE_TERRAIN)){ - hitTerrain = true; - // System.out.println("Impulse force: " + impulseForce); -// System.out.println("Position: " + position); - } if(impulse.type.matches(Collidable.TYPE_ITEM)){ if(ServerGravityTree.getServerGravityTree(parent)!=null){ ServerGravityTree.getServerGravityTree(parent).start(); } } if(impulse.type.matches(Collidable.TYPE_CREATURE)){ -// System.out.println(System.currentTimeMillis() + " creature hit!"); if(ServerGravityTree.getServerGravityTree(parent)!=null){ ServerGravityTree.getServerGravityTree(parent).start(); } } + if(impulse.type.matches(Collidable.TYPE_WORLD_BOUND)){ + this.resetGravityFall(); + } } Realm realm = Globals.realmManager.getEntityRealm(parent); //bound to world bounds @@ -100,6 +94,18 @@ public class ServerCollidableTree implements BehaviorTree { public static ServerCollidableTree getServerCollidableTree(Entity e){ return (ServerCollidableTree)e.getData(EntityDataStrings.SERVER_COLLIDABLE_TREE); } + + /** + * Adds a terrain collision to the collidable list + */ + protected void resetGravityFall(){ + if(ServerGravityTree.getServerGravityTree(parent)!=null){ + ServerGravityTree.getServerGravityTree(parent).stop(); + } + if(ServerFallTree.getFallTree(parent)!=null){ + ServerFallTree.getFallTree(parent).land(); + } + } } diff --git a/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java b/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java index 43e0b630..a5e3446a 100644 --- a/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java +++ b/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java @@ -63,6 +63,10 @@ public class ServerGravityTree implements BehaviorTree { if(state == GravityTreeState.NOT_ACTIVE){ frameCurrent = 0; } + ServerFallTree fallTree; + if((fallTree = ServerFallTree.getFallTree(parent))!=null){ + fallTree.start(); + } } public void interrupt(){ diff --git a/src/test/java/electrosphere/entity/state/movement/fall/ClientFallTreeTests.java b/src/test/java/electrosphere/entity/state/movement/fall/ClientFallTreeTests.java new file mode 100644 index 00000000..d3149026 --- /dev/null +++ b/src/test/java/electrosphere/entity/state/movement/fall/ClientFallTreeTests.java @@ -0,0 +1,61 @@ +package electrosphere.entity.state.movement.fall; + +import static org.junit.jupiter.api.Assertions.*; + +import org.joml.Vector3d; + +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.state.movement.jump.ClientJumpTree; +import electrosphere.entity.types.creature.CreatureTemplate; +import electrosphere.entity.types.creature.CreatureUtils; +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.EntityTestTemplate; +import electrosphere.test.testutils.TestEngineUtils; +import static electrosphere.test.testutils.Assertions.*; + +/** + * Tests the fall tree + */ +public class ClientFallTreeTests extends EntityTestTemplate { + + @IntegrationTest + public void isFalling_AtRest_false(){ + Entity serverEntity = CreatureUtils.serverSpawnBasicCreature(Globals.realmManager.first(), new Vector3d(), "human", CreatureTemplate.createDefault("human")); + Entity clientEntity = TestEngineUtils.getClientEquivalent(serverEntity); + + ClientFallTree clientFallTree = ClientFallTree.getFallTree(clientEntity); + assertEquals(false, clientFallTree.isFalling()); + } + + @IntegrationTest + public void isFalling_AfterJump_true(){ + Entity serverEntity = CreatureUtils.serverSpawnBasicCreature(Globals.realmManager.first(), new Vector3d(), "human", CreatureTemplate.createDefault("human")); + Entity clientEntity = TestEngineUtils.getClientEquivalent(serverEntity); + + ClientFallTree clientFallTree = ClientFallTree.getFallTree(clientEntity); + ClientJumpTree clientJumpTree = ClientJumpTree.getClientJumpTree(clientEntity); + clientJumpTree.start(); + + //make sure we're in in the air + assertEventually(() -> clientFallTree.isFalling(), 100); + } + + @IntegrationTest + public void isFalling_AfterLand_false(){ + Entity serverEntity = CreatureUtils.serverSpawnBasicCreature(Globals.realmManager.first(), new Vector3d(), "human", CreatureTemplate.createDefault("human")); + Entity clientEntity = TestEngineUtils.getClientEquivalent(serverEntity); + + ClientFallTree clientFallTree = ClientFallTree.getFallTree(clientEntity); + ClientJumpTree clientJumpTree = ClientJumpTree.getClientJumpTree(clientEntity); + clientJumpTree.start(); + + //make sure we're in in the air + TestEngineUtils.simulateFrames(3); + + assertEventually(() -> { + return !clientFallTree.isFalling(); + }); + } + +} diff --git a/src/test/java/electrosphere/entity/state/movement/fall/ServerFallTreeTests.java b/src/test/java/electrosphere/entity/state/movement/fall/ServerFallTreeTests.java new file mode 100644 index 00000000..afa68c01 --- /dev/null +++ b/src/test/java/electrosphere/entity/state/movement/fall/ServerFallTreeTests.java @@ -0,0 +1,61 @@ +package electrosphere.entity.state.movement.fall; + +import static org.junit.jupiter.api.Assertions.*; + + +import org.joml.Vector3d; + +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.state.movement.jump.ServerJumpTree; +import electrosphere.entity.types.creature.CreatureTemplate; +import electrosphere.entity.types.creature.CreatureUtils; +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.EntityTestTemplate; +import static electrosphere.test.testutils.Assertions.*; +import electrosphere.test.testutils.TestEngineUtils; + +/** + * Tests the server fall tree + */ +public class ServerFallTreeTests extends EntityTestTemplate { + + @IntegrationTest + public void isFalling_AtRest_false(){ + Entity creature = CreatureUtils.serverSpawnBasicCreature(Globals.realmManager.first(), new Vector3d(0,0,0), "human", CreatureTemplate.createDefault("human")); + + ServerFallTree serverFallTree = ServerFallTree.getFallTree(creature); + assertEquals(false, serverFallTree.isFalling()); + } + + @IntegrationTest + public void isFalling_AfterJump_true(){ + Entity creature = CreatureUtils.serverSpawnBasicCreature(Globals.realmManager.first(), new Vector3d(0,0,0), "human", CreatureTemplate.createDefault("human")); + + ServerFallTree serverFallTree = ServerFallTree.getFallTree(creature); + ServerJumpTree serverJumpTree = ServerJumpTree.getServerJumpTree(creature); + serverJumpTree.start(); + + //make sure we're in in the air + TestEngineUtils.waitForCondition(() -> !serverJumpTree.isJumping(), 100); + + assertEquals(true, serverFallTree.isFalling()); + } + + @IntegrationTest + public void isFalling_AfterLand_false(){ + Entity creature = CreatureUtils.serverSpawnBasicCreature(Globals.realmManager.first(), new Vector3d(0,0,0), "human", CreatureTemplate.createDefault("human")); + + ServerFallTree serverFallTree = ServerFallTree.getFallTree(creature); + ServerJumpTree serverJumpTree = ServerJumpTree.getServerJumpTree(creature); + serverJumpTree.start(); + + //make sure we're in in the air + TestEngineUtils.simulateFrames(3); + + assertEventually(() -> { + return !serverFallTree.isFalling(); + }); + } + +} diff --git a/src/test/java/electrosphere/test/template/RenderingTestTemplate.java b/src/test/java/electrosphere/test/template/RenderingTestTemplate.java index 6cb1b6a7..b7651eb7 100644 --- a/src/test/java/electrosphere/test/template/RenderingTestTemplate.java +++ b/src/test/java/electrosphere/test/template/RenderingTestTemplate.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.ExtendWith; import electrosphere.test.template.extensions.RenderingExtension; +import static electrosphere.test.testutils.Assertions.*; import electrosphere.test.testutils.TestRenderingUtils; /** @@ -26,7 +27,7 @@ public class RenderingTestTemplate { String canonicalName = this.getClass().getCanonicalName(); //check the render - TestRenderingUtils.assertEqualsRender(existingRenderPath, () -> { + assertEqualsRender(existingRenderPath, () -> { //on failure, save the failed render String failureSavePath = "./.testcache/" + canonicalName + "-" + renderName + ".png"; diff --git a/src/test/java/electrosphere/test/testutils/Assertions.java b/src/test/java/electrosphere/test/testutils/Assertions.java new file mode 100644 index 00000000..3f469f77 --- /dev/null +++ b/src/test/java/electrosphere/test/testutils/Assertions.java @@ -0,0 +1,110 @@ +package electrosphere.test.testutils; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; +import java.io.IOException; + +import java.util.function.Supplier; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; + +import electrosphere.engine.Globals; + +/** + * Custom assertion macros + */ +public class Assertions { + + /** + * The threshold at which we say the colors are 'close enough' + */ + static final int COLOR_COMPARE_THRESHOLD = 3; + + /** + * Asserts that the most recent render matches the image stored at the provided filepath + * @param existingRenderPath The filepath of the existing render + */ + public static void assertEqualsRender(String existingRenderPath, Runnable onFailure){ + BufferedImage testData = null; + try { + testData = ImageIO.read(new File(existingRenderPath)); + } catch (IOException e){ + fail("Failed to read existing image path " + existingRenderPath); + } + BufferedImage screenshot = Globals.renderingEngine.defaultFramebuffer.getPixels(Globals.renderingEngine.getOpenGLState()); + //check basic data + // + //width + if(testData.getWidth() != screenshot.getWidth()){ + onFailure.run(); + } + assertEquals(testData.getWidth(), screenshot.getWidth()); + + // + //height + if(testData.getHeight() != screenshot.getHeight()){ + onFailure.run(); + } + assertEquals(testData.getHeight(), screenshot.getHeight()); + + // + //pixel-by-pixel check + // + for(int x = 0; x < testData.getWidth(); x++){ + for(int y = 0; y < testData.getHeight(); y++){ + + //get from-disk rgba + int sourceRed = testData.getRGB(x, y) & 0xff; + int sourceGreen = (testData.getRGB(x, y) & 0xff00) >> 8; + int sourceBlue = (testData.getRGB(x, y) & 0xff0000) >> 16; + int sourceAlpha = (testData.getRGB(x, y) & 0xff000000) >>> 24; + + //get from-render rgba + int renderRed = screenshot.getRGB(x, y) & 0xff; + int renderGreen = (screenshot.getRGB(x, y) & 0xff00) >> 8; + int renderBlue = (screenshot.getRGB(x, y) & 0xff0000) >> 16; + int renderAlpha = (screenshot.getRGB(x, y) & 0xff000000) >>> 24; + + if( + Math.abs(sourceRed - renderRed) > COLOR_COMPARE_THRESHOLD || + Math.abs(sourceGreen - renderGreen) > COLOR_COMPARE_THRESHOLD || + Math.abs(sourceBlue - renderBlue) > COLOR_COMPARE_THRESHOLD || + Math.abs(sourceAlpha - renderAlpha) > COLOR_COMPARE_THRESHOLD + ){ + + onFailure.run(); + String failMessage = "Colors aren't approximately the same!\n" + + "Color from disk: " + sourceRed + "," + sourceGreen + "," + sourceBlue + "," + sourceAlpha + "\n" + + "Color from render: " + renderRed + "," + renderGreen + "," + renderBlue + "," + renderAlpha + "\n" + ; + fail(failMessage); + } + } + } + } + + /** + * Asserts that some test is true within a given number of frame simulations + * @param test The test + * @param maxFrames The number of frames + */ + public static void assertEventually(Supplier test, int maxFrames){ + int frameCount = 0; + boolean testResult = false; + while(!(testResult = test.get()) && frameCount < maxFrames){ + TestEngineUtils.simulateFrames(1); + frameCount++; + } + org.junit.jupiter.api.Assertions.assertTrue(testResult); + } + + /** + * Asserts that some runnable + * @param test + */ + public static void assertEventually(Supplier test){ + assertEventually(test, 100); + } + +} diff --git a/src/test/java/electrosphere/test/testutils/TestEngineUtils.java b/src/test/java/electrosphere/test/testutils/TestEngineUtils.java index 82df5a36..757d1202 100644 --- a/src/test/java/electrosphere/test/testutils/TestEngineUtils.java +++ b/src/test/java/electrosphere/test/testutils/TestEngineUtils.java @@ -1,8 +1,13 @@ package electrosphere.test.testutils; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.function.Supplier; + import electrosphere.engine.Globals; import electrosphere.engine.Main; import electrosphere.engine.profiler.Profiler; +import electrosphere.entity.Entity; import electrosphere.net.NetUtils; /** @@ -57,4 +62,53 @@ public class TestEngineUtils { } } + + /** + * The maximum number of frames to wait before an entity propagates to the client + */ + static final int MAX_FRAMES_TO_WAIT_FOR_CLIENT_PROPAGATION = 10; + + /** + * Gets the client equivalent of an entity + * @param serverEntity The server entity + * @return The client entity + */ + public static Entity getClientEquivalent(Entity serverEntity){ + int frames = 0; + while(frames < MAX_FRAMES_TO_WAIT_FOR_CLIENT_PROPAGATION && !Globals.clientSceneWrapper.containsServerId(serverEntity.getId())){ + TestEngineUtils.simulateFrames(1); + } + if(Globals.clientSceneWrapper.containsServerId(serverEntity.getId())){ + Entity rVal = Globals.clientSceneWrapper.getEntityFromServerId(serverEntity.getId()); + if(rVal == null){ + fail("Failed to find client entity at server id lookup for id: " + serverEntity.getId()); + } + return rVal; + } + fail("Failed to find client entity at server id lookup for id: " + serverEntity.getId()); + return null; + } + + /** + * Waits for a given test to be true + * @param test The test to wait for + */ + public static void waitForCondition(Supplier test, int maxFrames){ + int frameCount = 0; + boolean testResult = false; + while(!(testResult = test.get()) && frameCount < maxFrames){ + TestEngineUtils.simulateFrames(1); + frameCount++; + } + org.junit.jupiter.api.Assertions.assertTrue(testResult); + } + + /** + * Waits for a given test to be true + * @param test The test to wait for + */ + public static void waitForCondition(Supplier test){ + waitForCondition(test,100); + } + } diff --git a/src/test/java/electrosphere/test/testutils/TestRenderingUtils.java b/src/test/java/electrosphere/test/testutils/TestRenderingUtils.java index 86761d06..8d41ca6c 100644 --- a/src/test/java/electrosphere/test/testutils/TestRenderingUtils.java +++ b/src/test/java/electrosphere/test/testutils/TestRenderingUtils.java @@ -6,8 +6,6 @@ import java.io.IOException; import javax.imageio.ImageIO; -import static org.junit.jupiter.api.Assertions.*; - import electrosphere.engine.Globals; /** @@ -15,74 +13,6 @@ import electrosphere.engine.Globals; */ public class TestRenderingUtils { - /** - * The threshold at which we say the colors are 'close enough' - */ - static final int COLOR_COMPARE_THRESHOLD = 3; - - /** - * Asserts that the most recent render matches the image stored at the provided filepath - * @param existingRenderPath The filepath of the existing render - */ - public static void assertEqualsRender(String existingRenderPath, Runnable onFailure){ - BufferedImage testData = null; - try { - testData = ImageIO.read(new File(existingRenderPath)); - } catch (IOException e){ - fail("Failed to read existing image path " + existingRenderPath); - } - BufferedImage screenshot = Globals.renderingEngine.defaultFramebuffer.getPixels(Globals.renderingEngine.getOpenGLState()); - //check basic data - // - //width - if(testData.getWidth() != screenshot.getWidth()){ - onFailure.run(); - } - assertEquals(testData.getWidth(), screenshot.getWidth()); - - // - //height - if(testData.getHeight() != screenshot.getHeight()){ - onFailure.run(); - } - assertEquals(testData.getHeight(), screenshot.getHeight()); - - // - //pixel-by-pixel check - // - for(int x = 0; x < testData.getWidth(); x++){ - for(int y = 0; y < testData.getHeight(); y++){ - - //get from-disk rgba - int sourceRed = testData.getRGB(x, y) & 0xff; - int sourceGreen = (testData.getRGB(x, y) & 0xff00) >> 8; - int sourceBlue = (testData.getRGB(x, y) & 0xff0000) >> 16; - int sourceAlpha = (testData.getRGB(x, y) & 0xff000000) >>> 24; - - //get from-render rgba - int renderRed = screenshot.getRGB(x, y) & 0xff; - int renderGreen = (screenshot.getRGB(x, y) & 0xff00) >> 8; - int renderBlue = (screenshot.getRGB(x, y) & 0xff0000) >> 16; - int renderAlpha = (screenshot.getRGB(x, y) & 0xff000000) >>> 24; - - if( - Math.abs(sourceRed - renderRed) > COLOR_COMPARE_THRESHOLD || - Math.abs(sourceGreen - renderGreen) > COLOR_COMPARE_THRESHOLD || - Math.abs(sourceBlue - renderBlue) > COLOR_COMPARE_THRESHOLD || - Math.abs(sourceAlpha - renderAlpha) > COLOR_COMPARE_THRESHOLD - ){ - - onFailure.run(); - String failMessage = "Colors aren't approximately the same!\n" + - "Color from disk: " + sourceRed + "," + sourceGreen + "," + sourceBlue + "," + sourceAlpha + "\n" + - "Color from render: " + renderRed + "," + renderGreen + "," + renderBlue + "," + renderAlpha + "\n" - ; - fail(failMessage); - } - } - } - } - /** * Used for saving a copy of the current render (ie for generating test data) * @param existingRenderPath The filepath of the existing render