fall tree tests, testing utils, assertion macros
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
This commit is contained in:
parent
9cdfac7898
commit
eff082ca76
@ -98,14 +98,17 @@ public class MovementAudioService {
|
|||||||
* @return The path to the audio file to play
|
* @return The path to the audio file to play
|
||||||
*/
|
*/
|
||||||
public String getAudioPath(int voxelType, InteractionType type){
|
public String getAudioPath(int voxelType, InteractionType type){
|
||||||
|
String rVal = null;
|
||||||
SurfaceAudioType surfaceAudio = this.defaultSurfaceAudio;
|
SurfaceAudioType surfaceAudio = this.defaultSurfaceAudio;
|
||||||
|
|
||||||
//Check if ignored
|
//Check if ignored
|
||||||
|
if(this.ignoredVoxelTypes != null){
|
||||||
for(int ignoredVoxelType : this.ignoredVoxelTypes){
|
for(int ignoredVoxelType : this.ignoredVoxelTypes){
|
||||||
if(ignoredVoxelType == voxelType){
|
if(ignoredVoxelType == voxelType){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//gets the surface audio definition
|
//gets the surface audio definition
|
||||||
if(surfaceAudioMap.containsKey(voxelType)){
|
if(surfaceAudioMap.containsKey(voxelType)){
|
||||||
@ -115,6 +118,7 @@ public class MovementAudioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//gets the list to pull from
|
//gets the list to pull from
|
||||||
|
if(surfaceAudio != null){
|
||||||
List<String> availableFiles = surfaceAudio.getFootstepRegularBareAudioPaths();
|
List<String> availableFiles = surfaceAudio.getFootstepRegularBareAudioPaths();
|
||||||
switch(type){
|
switch(type){
|
||||||
case STEP_BARE_REG: {
|
case STEP_BARE_REG: {
|
||||||
@ -136,10 +140,11 @@ public class MovementAudioService {
|
|||||||
availableFiles = surfaceAudio.getLandAudioPaths();
|
availableFiles = surfaceAudio.getLandAudioPaths();
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//return the audio
|
|
||||||
int roll = random.nextInt(availableFiles.size());
|
int roll = random.nextInt(availableFiles.size());
|
||||||
return availableFiles.get(roll);
|
rVal = availableFiles.get(roll);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -466,10 +466,13 @@ public class CollisionEngine {
|
|||||||
DBody rigidBody = PhysicsEntityUtils.getDBody(physicsEntity);
|
DBody rigidBody = PhysicsEntityUtils.getDBody(physicsEntity);
|
||||||
Matrix4d inverseTransform = new Matrix4d();
|
Matrix4d inverseTransform = new Matrix4d();
|
||||||
Vector4d rawPos = inverseTransform.transform(new Vector4d(PhysicsUtils.getRigidBodyPosition(rigidBody),1));
|
Vector4d rawPos = inverseTransform.transform(new Vector4d(PhysicsUtils.getRigidBodyPosition(rigidBody),1));
|
||||||
Vector3d newPosition = new Vector3d(rawPos.x,rawPos.y,rawPos.z);
|
Vector3d calculatedPosition = new Vector3d(rawPos.x,rawPos.y,rawPos.z);
|
||||||
newPosition = this.suggestMovementPosition(collisionWorldData, newPosition);
|
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);
|
Quaterniond newRotation = PhysicsUtils.getRigidBodyRotation(rigidBody);
|
||||||
EntityUtils.getPosition(physicsEntity).set(newPosition);
|
EntityUtils.getPosition(physicsEntity).set(suggestedPosition);
|
||||||
EntityUtils.getRotation(physicsEntity).set(newRotation);
|
EntityUtils.getRotation(physicsEntity).set(newRotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,6 +47,9 @@ public class Collidable {
|
|||||||
public static final String TYPE_FOLIAGE_STATIC = "foliageStatic";
|
public static final String TYPE_FOLIAGE_STATIC = "foliageStatic";
|
||||||
public static final long TYPE_FOLIAGE_BIT = 0x80;
|
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
|
* Constructor
|
||||||
|
|||||||
@ -44,29 +44,23 @@ public class ServerCollidableTree implements BehaviorTree {
|
|||||||
public void simulate(float deltaTime){
|
public void simulate(float deltaTime){
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Quaterniond rotation = EntityUtils.getRotation(parent);
|
Quaterniond rotation = EntityUtils.getRotation(parent);
|
||||||
Vector3d offsetVector = new Vector3d();
|
|
||||||
Vector3d newPosition = new Vector3d(position);
|
Vector3d newPosition = new Vector3d(position);
|
||||||
//have we hit a terrain impulse?
|
//have we hit a terrain impulse?
|
||||||
boolean hitTerrain = false;
|
|
||||||
//handle impulses
|
//handle impulses
|
||||||
for(Impulse impulse : collidable.getImpulses()){
|
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(impulse.type.matches(Collidable.TYPE_ITEM)){
|
||||||
if(ServerGravityTree.getServerGravityTree(parent)!=null){
|
if(ServerGravityTree.getServerGravityTree(parent)!=null){
|
||||||
ServerGravityTree.getServerGravityTree(parent).start();
|
ServerGravityTree.getServerGravityTree(parent).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(impulse.type.matches(Collidable.TYPE_CREATURE)){
|
if(impulse.type.matches(Collidable.TYPE_CREATURE)){
|
||||||
// System.out.println(System.currentTimeMillis() + " creature hit!");
|
|
||||||
if(ServerGravityTree.getServerGravityTree(parent)!=null){
|
if(ServerGravityTree.getServerGravityTree(parent)!=null){
|
||||||
ServerGravityTree.getServerGravityTree(parent).start();
|
ServerGravityTree.getServerGravityTree(parent).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(impulse.type.matches(Collidable.TYPE_WORLD_BOUND)){
|
||||||
|
this.resetGravityFall();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
||||||
//bound to world bounds
|
//bound to world bounds
|
||||||
@ -101,5 +95,17 @@ public class ServerCollidableTree implements BehaviorTree {
|
|||||||
return (ServerCollidableTree)e.getData(EntityDataStrings.SERVER_COLLIDABLE_TREE);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,6 +63,10 @@ public class ServerGravityTree implements BehaviorTree {
|
|||||||
if(state == GravityTreeState.NOT_ACTIVE){
|
if(state == GravityTreeState.NOT_ACTIVE){
|
||||||
frameCurrent = 0;
|
frameCurrent = 0;
|
||||||
}
|
}
|
||||||
|
ServerFallTree fallTree;
|
||||||
|
if((fallTree = ServerFallTree.getFallTree(parent))!=null){
|
||||||
|
fallTree.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void interrupt(){
|
public void interrupt(){
|
||||||
|
|||||||
@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import org.junit.jupiter.api.Tag;
|
|||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import electrosphere.test.template.extensions.RenderingExtension;
|
import electrosphere.test.template.extensions.RenderingExtension;
|
||||||
|
import static electrosphere.test.testutils.Assertions.*;
|
||||||
import electrosphere.test.testutils.TestRenderingUtils;
|
import electrosphere.test.testutils.TestRenderingUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,7 +27,7 @@ public class RenderingTestTemplate {
|
|||||||
String canonicalName = this.getClass().getCanonicalName();
|
String canonicalName = this.getClass().getCanonicalName();
|
||||||
|
|
||||||
//check the render
|
//check the render
|
||||||
TestRenderingUtils.assertEqualsRender(existingRenderPath, () -> {
|
assertEqualsRender(existingRenderPath, () -> {
|
||||||
|
|
||||||
//on failure, save the failed render
|
//on failure, save the failed render
|
||||||
String failureSavePath = "./.testcache/" + canonicalName + "-" + renderName + ".png";
|
String failureSavePath = "./.testcache/" + canonicalName + "-" + renderName + ".png";
|
||||||
|
|||||||
110
src/test/java/electrosphere/test/testutils/Assertions.java
Normal file
110
src/test/java/electrosphere/test/testutils/Assertions.java
Normal file
@ -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<Boolean> 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<Boolean> test){
|
||||||
|
assertEventually(test, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,8 +1,13 @@
|
|||||||
package electrosphere.test.testutils;
|
package electrosphere.test.testutils;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
import electrosphere.engine.Globals;
|
||||||
import electrosphere.engine.Main;
|
import electrosphere.engine.Main;
|
||||||
import electrosphere.engine.profiler.Profiler;
|
import electrosphere.engine.profiler.Profiler;
|
||||||
|
import electrosphere.entity.Entity;
|
||||||
import electrosphere.net.NetUtils;
|
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<Boolean> 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<Boolean> test){
|
||||||
|
waitForCondition(test,100);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,6 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
import electrosphere.engine.Globals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,74 +13,6 @@ import electrosphere.engine.Globals;
|
|||||||
*/
|
*/
|
||||||
public class TestRenderingUtils {
|
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)
|
* Used for saving a copy of the current render (ie for generating test data)
|
||||||
* @param existingRenderPath The filepath of the existing render
|
* @param existingRenderPath The filepath of the existing render
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user