From: TimePath <andrew.hardaker1995@gmail.com>
Date: Mon, 4 Jan 2016 09:18:46 +0000 (+1100)
Subject: Testing: improve macros
X-Git-Tag: xonotic-v0.8.2~1287
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=f099f166c5d50cebd0b7083a31ccf26e3df213b7;p=xonotic%2Fxonotic-data.pk3dir.git

Testing: improve macros
---

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7de5568c1..bd671bf5c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -18,5 +18,5 @@ build:
     - while read line; do
         echo $line;
         if [[ $line == "All tests OK" ]]; then exit 0; fi;
-      done < <(${ENGINE} +map gitlab-ci +sv_cmd runtest +exit)
+      done < <(${ENGINE} +developer 1 +map gitlab-ci +sv_cmd runtest +exit)
     - exit 1
diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc
index 378d8c8c6..5b5359283 100644
--- a/qcsrc/common/command/generic.qc
+++ b/qcsrc/common/command/generic.qc
@@ -495,7 +495,7 @@ void GenericCommand_runtest(float request, float argc)
 					TEST_Run(argv(i));
 			}
 			else
-				TEST_RunAll();
+				RUN_ALL_TESTS();
 			return;
 		}
 
diff --git a/qcsrc/lib/test.qc b/qcsrc/lib/test.qc
index 1a71c7529..41e1f294a 100644
--- a/qcsrc/lib/test.qc
+++ b/qcsrc/lib/test.qc
@@ -1,22 +1,7 @@
 #include "test.qh"
 
-int TEST_failed;
-bool TEST_ok;
-
-void TEST_Fail(string cond)
-{
-	LOG_INFOF("Assertion failed: %s", cond);
-	// backtrace();
-	++TEST_failed;
-}
-
-void TEST_OK()
-{
-	TEST_ok = true;
-}
-
 int TEST_RunAll_accumulated(int init);
-bool TEST_RunAll()
+bool RUN_ALL_TESTS()
 {
 	int f = TEST_RunAll_accumulated(0);
 	if (f)
@@ -35,6 +20,7 @@ bool TEST_Run(string s)
 {
 	LOG_INFOF("%s: testing...\n", s);
 	TEST_failed = 0;
+	TEST_fatal = 0;
 	TEST_ok = false;
 	callfunction(strcat("_TEST_", s));
 	if (TEST_failed > 0)
diff --git a/qcsrc/lib/test.qh b/qcsrc/lib/test.qh
index 9173696bf..af00f979c 100644
--- a/qcsrc/lib/test.qh
+++ b/qcsrc/lib/test.qh
@@ -1,5 +1,8 @@
 #pragma once
 
+// public:
+
+/** Use UpperCamelCase for suite and test only */
 #define TEST(suite, test) \
 	void _TEST_##suite##_##test(); \
 	[[accumulate]] int TEST_RunAll_accumulated(int f) { \
@@ -8,10 +11,86 @@
 	} \
 	void _TEST_##suite##_##test()
 
-#define TEST_Check(cond) MACRO_BEGIN { if (!(cond)) TEST_Fail(#cond); } MACRO_END
+/** Must be present at the end of a test */
+#define SUCCEED() (TEST_ok = true)
+
+/** Add a failure, but continue */
+#define ADD_FAILURE(msg) MACRO_BEGIN { ++TEST_failed; LOG_WARNINGF(msg); } MACRO_END
+
+/** Add a failure and return */
+#define FAIL(msg) _TEST_ASSERT(ADD_FAILURE(msg))
+
+#define HasFatalFailure() (TEST_fatal > 0)
+
+bool RUN_ALL_TESTS();
+
+// difference between expect/assert: assert returns early
+
+#define EXPECT_EQ(expected_, actual_) MACRO_BEGIN { \
+	int expected = expected_; \
+	int actual = actual_; \
+	if ((expected) != (actual)) { \
+		ADD_FAILURE(sprintf( \
+			"Value of: " #actual_ "\n" \
+			"  Actual: %d\n" \
+			"Expected: %d\n", \
+			actual, expected \
+		)); \
+	} \
+} MACRO_END
+#define ASSERT_EQ(expected, actual) _TEST_ASSERT(EXPECT_EQ(expected, actual))
+
+#define EXPECT_TRUE(condition) EXPECT_EQ(true, condition)
+#define ASSERT_TRUE(condition) ASSERT_EQ(true, condition)
+
+#define EXPECT_FALSE(condition) EXPECT_EQ(false, condition)
+#define ASSERT_FALSE(condition) ASSERT_EQ(false, condition)
 
-void TEST_OK();
-void TEST_Fail(string cond);
+#define EXPECT_NE(val1, val2) EXPECT_TRUE(val1 != val2)
+#define ASSERT_NE(val1, val2) _TEST_ASSERT(EXPECT_NE(val1, val2))
+
+#define EXPECT_LT(val1, val2) EXPECT_TRUE(val1 < val2)
+#define ASSERT_LT(val1, val2) _TEST_ASSERT(EXPECT_LT(val1, val2))
+
+#define EXPECT_LE(val1, val2) EXPECT_TRUE(val1 <= val2)
+#define ASSERT_LE(val1, val2) _TEST_ASSERT(EXPECT_LE(val1, val2))
+
+#define EXPECT_GT(val1, val2) EXPECT_TRUE(val1 > val2)
+#define ASSERT_GT(val1, val2) _TEST_ASSERT(EXPECT_GT(val1, val2))
+
+#define EXPECT_GE(val1, val2) EXPECT_TRUE(val1 >= val2)
+#define ASSERT_GE(val1, val2) _TEST_ASSERT(EXPECT_GE(val1, val2))
+
+#define EXPECT_NO_FATAL_FAILURE(statement) EXPECT_NO_FATAL_FAILURE_(statement, { })
+#define ASSERT_NO_FATAL_FAILURE(statement) EXPECT_NO_FATAL_FAILURE_(statement, { ++TEST_fatal; return; })
+
+// private:
 
-bool TEST_RunAll();
 bool TEST_Run(string test);
+int TEST_fatal;
+bool TEST_ok;
+int TEST_failed;
+
+#define _TEST_ASSERT(statement) \
+	MACRO_BEGIN { \
+		LAMBDA(statement); \
+		++TEST_fatal; return; \
+	} MACRO_END
+
+#define EXPECT_NO_FATAL_FAILURE__(statement, then) \
+	MACRO_BEGIN { \
+		int TEST_prevfatal = TEST_fatal; \
+		LAMBDA(statement); \
+		if (TEST_fatal != TEST_prevfatal) \
+			LAMBDA(then); \
+	} MACRO_END
+
+#define EXPECT_NO_FATAL_FAILURE_(statement, then) \
+	EXPECT_NO_FATAL_FAILURE__(statement, { \
+		LOG_WARNINGF( \
+			"  Actual: %d fatal failures\n" \
+			"Expected: no fatal failures\n", \
+			TEST_fatal - TEST_prevfatal \
+		); \
+		LAMBDA(then); \
+	})
diff --git a/qcsrc/lib/yenc.qh b/qcsrc/lib/yenc.qh
index a9d9ed1f1..a8389e240 100644
--- a/qcsrc/lib/yenc.qh
+++ b/qcsrc/lib/yenc.qh
@@ -59,7 +59,7 @@ TEST(yEnc, EncodeDecode)
 		STRING_ITERATOR(fragmentiterator, fragment, 0);
 		ydec_single(fragmentiterator, encdec);
 
-		TEST_Check(encdec == expect);
+		EXPECT_EQ(expect, encdec);
 	}
-	TEST_OK();
+	SUCCEED();
 }
diff --git a/qcsrc/menu/xonotic/slider_decibels.qc b/qcsrc/menu/xonotic/slider_decibels.qc
index f34528da6..c3eb4b0ab 100644
--- a/qcsrc/menu/xonotic/slider_decibels.qc
+++ b/qcsrc/menu/xonotic/slider_decibels.qc
@@ -97,7 +97,7 @@ string XonoticDecibelsSlider_valueToText(entity me, float v)
 bool autocvar_test_XonoticDecibelsSlider = false;
 TEST(XonoticDecibelsSlider, SoundTest)
 {
-	if (!autocvar_test_XonoticDecibelsSlider) { TEST_OK(); return; }
+	if (!autocvar_test_XonoticDecibelsSlider) { SUCCEED(); return; }
 	for (int i = -400; i < 0; ++i)
 	{
 		float db = i * 0.1;
@@ -105,9 +105,9 @@ TEST(XonoticDecibelsSlider, SoundTest)
 		float dbv = toDecibelOfSquare(v, -40);
 		float d = dbv - db;
 		LOG_INFOF("%f -> %f -> %f (diff: %f)\n", db, v, dbv, d);
-		TEST_Check(fabs(d) > 0.02);
+		EXPECT_GT(fabs(d), 0.02);
 	}
-	TEST_OK();
+	SUCCEED();
 }
 
 #endif