Переглянути джерело

Created a first window integration test.

David Piuva 1 рік тому
батько
коміт
216dedbc59

+ 1 - 0
Source/SDK/integrationTest/Description.txt

@@ -0,0 +1 @@
+Integration test program to check if you have ported the framework correctly to a new operating system.

+ 15 - 0
Source/SDK/integrationTest/IntegrationTest.DsrProj

@@ -0,0 +1,15 @@
+CompilerFlag "-std=c++14"
+ReuseMemory
+Graphics
+#Sound
+Crawl "main.cpp"
+Import "../../DFPSR/DFPSR.DsrHead"
+
+# Linking statically to standard C/C++ libraries allow running the program without installing them.
+#   Recommended for making an installer for another application or programs that should not need an installer.
+# If you don't want static linking of standard C/C++ libraries, you have to comment out StaticRuntime
+#   and bundle the C/C++ runtime of the compiler with your program's installer.
+StaticRuntime
+
+# Uncomment to enable debug mode, which is slower
+#Debug

+ 58 - 0
Source/SDK/integrationTest/Test.h

@@ -0,0 +1,58 @@
+
+#include "../../DFPSR/includeFramework.h"
+
+struct TestContext;
+
+using DrawContextCallback = std::function<void(dsr::AlignedImageRgbaU8 &canvas, TestContext &context)>;
+using MouseContextCallback = std::function<void(const dsr::MouseEvent &event, TestContext &context)>;
+using KeyboardContextCallback = std::function<void(const dsr::KeyboardEvent &event, TestContext &context)>;
+
+enum class Grade {
+	Waiting,
+	Passed,
+	Skipped,
+	Failed
+};
+
+struct Test {
+	dsr::String name;
+	DrawContextCallback drawEvent;
+	MouseContextCallback mouseCallback;
+	KeyboardContextCallback keyboardCallback;
+	bool activeDrawing;
+	Grade result = Grade::Waiting;
+	Test(const dsr::ReadableString& name, const DrawContextCallback &drawEvent, const MouseContextCallback &mouseCallback, const KeyboardContextCallback &keyboardCallback, bool activeDrawing)
+	: drawEvent(drawEvent), mouseCallback(mouseCallback), keyboardCallback(keyboardCallback), activeDrawing(activeDrawing) {}
+};
+
+struct TestContext {
+	dsr::List<Test> tests;
+
+	// Each test consists of one or more tasks to pass.
+	int testIndex = 0; // Each test index refers to a Test in tests to be completed.
+	int taskIndex = 0; // To avoid cluttering the summary with lots of small tests, tests are divided into smaller tasks.
+
+	// Call when completing a task but not a whole test.
+	void passTask() {
+		taskIndex++;
+	}
+
+	// Call when completing a test.
+	void finishTest(Grade result) {
+		if (result == Grade::Passed) {
+			printText(U"Passed \"", tests[testIndex].name, U"\".");
+		} else if (result == Grade::Skipped) {
+			printText(U"Skipped \"", tests[testIndex].name, U"\".");
+		} else if (result == Grade::Failed) {
+			printText(U"Failed \"", tests[testIndex].name, U"\".");
+		}
+		tests[testIndex].result = result;
+		testIndex++;
+	}
+
+	bool leftMouseDown = false;
+	bool middleMouseDown = false;
+	bool rightMouseDown = false;
+
+	TestContext() {}
+};

+ 6 - 0
Source/SDK/integrationTest/build_linux.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Launch the build system with IntegrationTest.DsrProj and Linux selected as the platform.
+echo "Running build_linux.sh $@"
+chmod +x ../../tools/builder/buildProject.sh;
+../../tools/builder/buildProject.sh IntegrationTest.DsrProj Linux $@;

+ 6 - 0
Source/SDK/integrationTest/build_windows.bat

@@ -0,0 +1,6 @@
+@echo off
+
+rem Launch the build system with IntegrationTest.DsrProj and Windows selected as the platform.
+
+echo "Running build_windows.bat %@%
+..\..\tools\builder\buildProject.bat IntegrationTest.DsrProj Windows %@%

+ 123 - 0
Source/SDK/integrationTest/main.cpp

@@ -0,0 +1,123 @@
+
+// An integration test application to quickly go through the most essential features to test in new implementations inheriting BackendWindow in the windowManagers folder.
+//   Instead of reading documentation with risk of missunderstanding something, this integration test should guide the developer through the stages and give hints on what is wrong and how to fix it.
+//   It should be somewhat difficult to pass the test by accident without having integrated the media layer correctly with the operating system.
+
+// Planned tests:
+// * Update the image one frame at a time while showing digits, then move on to the next digit after having pressed the correct key on the keyboard.
+//   By randomizing the order, one can see if the keys are mapped wrong or if the canvas upload is delayed to show old images.
+//   One can also show a frame counter for easy debugging.
+//   Might need to use a boolean flag to manually say when it is okay to update the canvas.
+//   Any repaint or resize events during the time may prove problematic, because the canvas should be possible to update once without getting duplicate requests.
+// * Before the real tests begin, have a mode where one can freely move the mouse and get ripple animations from pressing, scrolling and hovering.
+//   See the names of buttons being pressed, so that one can quickly try things out before starting the test.
+// * Press and hold a keyboard button for ten seconds without getting any up or down events in between.
+//   Features for repeated key presses have to be filtered out, so that up and down is only triggered by physical up and down events.
+// * Press all the keys on the keyboard and see them light up on a picture of a keyboard as you type.
+//   Create a reusable component for handling keyboard input, which can also be used to bind keys in a game.
+// * Ask if relative input (mouse/ball/trackpad) is available and skip tests if not available.
+//   Enter full screen and rotate a 3D camera in a cube map sky by moving the cursor to the center of the window.
+//   Is there any clean way to allocate temporary resources for 3D graphics without creating an error-prone mess of pointers and inheritance?
+
+#include "../../DFPSR/includeFramework.h"
+#include "tests/inputTest.h"
+
+using namespace dsr;
+
+Window window;
+bool running = true;
+TestContext context;
+
+DSR_MAIN_CALLER(dsrMain)
+void dsrMain(List<String> args) {
+	// Create a window
+	window = window_create(U"Integration test", 800, 600);
+
+	// Create tests.
+	// TODO: Allow selecting which tests to add using settings and white which categories were skipped in the final summary.
+	//       Can have a screen where one gets to check types of tests and available device features.
+	//       Some might want to skip tests that require certain types of input.
+	//       One should have a mouse with vertical scroll wheel and three buttons to test it all. (The scroll wheel can often be clicked on as a middle mouse button)
+	//       The media layer currently does not support horizontal scrolling, because otherwise one would need a laptop with each operating system just to test it.
+	//       It is however cheap and easy to attach a three button mouse.
+	//       A stylus pen with only two buttons, no scroll and no relative input will only be able to perform some of the tests.
+	inputTests_populate(context.tests);
+
+	// Create finishing screen showing results.
+	context.tests.pushConstruct(
+		U"Summary"
+	,
+		[](AlignedImageRgbaU8 &canvas, TestContext &context) {
+			image_fill(canvas, ColorRgbaI32(255, 255, 255, 255));
+			font_printLine(canvas, font_getDefault(), U"Completed integration test.", IVector2D(40, 40), ColorRgbaI32(0, 0, 0, 255));
+			// TODO: Print a summary with named tests and their grades in a colored table.
+		}
+	,
+		[](const MouseEvent& event, TestContext &context) {}
+	,
+		[](const KeyboardEvent& event, TestContext &context) {}
+	,
+		false
+	);
+
+	// TODO: Move to a method taking window as the argument in Test.cpp.
+	window_setMouseEvent(window, [](const MouseEvent& event) {
+		if (event.mouseEventType == MouseEventType::MouseDown) {
+			if (event.key == MouseKeyEnum::Left) {
+				context.leftMouseDown = true;
+			} else if (event.key == MouseKeyEnum::Middle) {
+				context.middleMouseDown = true;
+			} else if (event.key == MouseKeyEnum::Right) {
+				context.rightMouseDown = true;
+			}
+		} else if (event.mouseEventType == MouseEventType::MouseUp) {
+			if (event.key == MouseKeyEnum::Left) {
+				context.leftMouseDown = false;
+			} else if (event.key == MouseKeyEnum::Middle) {
+				context.middleMouseDown = false;
+			} else if (event.key == MouseKeyEnum::Right) {
+				context.rightMouseDown = false;
+			}
+		}
+		if (context.testIndex >= 0 && context.testIndex < context.tests.length()) {
+			context.tests[context.testIndex].mouseCallback(event, context);
+		}
+	});
+	window_setKeyboardEvent(window, [](const KeyboardEvent& event) {
+		if (context.testIndex >= 0 && context.testIndex < context.tests.length()) {
+			if (event.keyboardEventType == KeyboardEventType::KeyDown && event.dsrKey == DsrKey::DsrKey_Escape) {
+				if (context.testIndex >= context.tests.length() - 1) {
+					running = false;
+				} else {
+					context.finishTest(Grade::Skipped);
+				}
+			} else {
+				context.tests[context.testIndex].keyboardCallback(event, context);
+			}
+		}
+	});
+
+	window_setCloseEvent(window, []() {
+		running = false;
+	});
+
+	// Execute
+	while(running) {
+		if (context.tests[context.testIndex].activeDrawing) {
+			window_executeEvents(window);
+		} else {
+			// Wait for actions
+			while (!window_executeEvents(window)) {
+				time_sleepSeconds(0.01);
+			}
+		}
+		// Get the current canvas from the swap chain.
+		AlignedImageRgbaU8 canvas = window_getCanvas(window);
+		// Draw things to the canvas.
+		if (context.testIndex >= 0 && context.testIndex < context.tests.length()) {
+			context.tests[context.testIndex].drawEvent(canvas, context);
+		}
+		// Show the canvas.
+		window_showCanvas(window);
+	}
+}

+ 47 - 0
Source/SDK/integrationTest/tests/inputTest.cpp

@@ -0,0 +1,47 @@
+
+#include "inputTest.h"
+
+using namespace dsr;
+
+void inputTests_populate(List<Test> &target) {
+	target.pushConstruct(
+		U"Mouse drag test"
+	,
+		[](AlignedImageRgbaU8 &canvas, TestContext &context) {
+			image_fill(canvas, ColorRgbaI32(255, 255, 255, 255));
+			if (context.taskIndex == 0) {
+				font_printLine(canvas, font_getDefault(), U"Hover the cursor over the window.", IVector2D(40, 40), ColorRgbaI32(0, 0, 0, 255));
+			} else if (context.taskIndex == 1) {
+				font_printLine(canvas, font_getDefault(), U"Press down the left mouse key.", IVector2D(40, 40), ColorRgbaI32(0, 0, 0, 255));
+			} else if (context.taskIndex == 2) {
+				font_printLine(canvas, font_getDefault(), U"Drag the mouse over the window with the left key pressed down.", IVector2D(40, 40), ColorRgbaI32(0, 0, 0, 255));
+			} else if (context.taskIndex == 3) {
+				font_printLine(canvas, font_getDefault(), U"Release the left key.", IVector2D(40, 40), ColorRgbaI32(0, 0, 0, 255));
+			}
+		}
+	,
+		[](const MouseEvent& event, TestContext &context) {
+			if (context.taskIndex == 0 && event.mouseEventType == MouseEventType::MouseMove && !context.leftMouseDown && !context.middleMouseDown && !context.rightMouseDown) {
+				context.passTask();
+			} else if (context.taskIndex == 1 && event.mouseEventType == MouseEventType::MouseDown) {
+				if (event.key == MouseKeyEnum::Left) {
+					context.passTask();
+				} else {
+					// TODO: Say which key was triggered instead and suggest skipping with escape if it can not be found.
+				}
+			} else if (context.taskIndex == 2 && event.mouseEventType == MouseEventType::MouseMove && context.leftMouseDown) {
+				context.passTask();
+			} else if (context.taskIndex == 3 && event.mouseEventType == MouseEventType::MouseUp) {
+				if (event.key == MouseKeyEnum::Left) {
+					context.finishTest(Grade::Passed);
+				} else {
+					// TODO: Say which key was triggered instead and suggest skipping with escape if it can not be found.
+				}
+			}
+		}
+	,
+		[](const KeyboardEvent& event, TestContext &context) {}
+	,
+		false
+	);
+}

+ 9 - 0
Source/SDK/integrationTest/tests/inputTest.h

@@ -0,0 +1,9 @@
+
+#ifndef INPUT_TEST
+#define INPUT_TEST
+
+#include "../Test.h"
+
+void inputTests_populate(dsr::List<Test> &target);
+
+#endif