2020-10-03 18:32:01 -04:00
|
|
|
// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#value-parameterized-tests
|
|
|
|
|
|
|
|
#include <thread>
|
|
|
|
#include <chrono>
|
|
|
|
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "game/runtime.h"
|
|
|
|
#include "goalc/listener/Listener.h"
|
|
|
|
#include "goalc/compiler/Compiler.h"
|
|
|
|
|
|
|
|
#include "third-party/inja.hpp"
|
|
|
|
#include "third-party/json.hpp"
|
|
|
|
|
2020-10-09 19:52:09 -04:00
|
|
|
#include <test/goalc/framework/test_runner.h>
|
2020-10-03 18:32:01 -04:00
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <string>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <sstream>
|
|
|
|
#include <iostream>
|
|
|
|
#include <random>
|
|
|
|
|
|
|
|
// --------
|
|
|
|
// This is a very over-engineered integer test, but it serves as a decent example of how to use the
|
|
|
|
// test+template framework
|
|
|
|
// I've heavily annotated it with comments and links to docs to help
|
|
|
|
// --------
|
|
|
|
|
|
|
|
// We are using Google Test's paramaterized test feature
|
|
|
|
// This allows us to define a single generic test, and pass in a whole bunch of values
|
2020-10-09 13:24:55 -04:00
|
|
|
// See -
|
|
|
|
// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#value-parameterized-tests
|
2020-10-03 18:32:01 -04:00
|
|
|
struct IntegerParam {
|
2020-10-09 13:24:55 -04:00
|
|
|
// An index is needed to be explicitly set because I couldn't find a way to pull the test-index
|
|
|
|
// number from google's API
|
|
|
|
// TODO - if you can find a way, please improve!
|
|
|
|
// But this is needed so we can uniquely save the template files, especially if they error out
|
|
|
|
// Why? - since you may choose to generate random values, it's nice for them to be stored after
|
|
|
|
// the tests complete. Some tests may be complex as well
|
2020-10-03 18:32:01 -04:00
|
|
|
int index;
|
2020-10-09 13:24:55 -04:00
|
|
|
// Each integer test has a signed value, and can be represented as hex or an integral
|
2020-10-03 18:32:01 -04:00
|
|
|
s64 val;
|
|
|
|
bool hex;
|
|
|
|
|
|
|
|
IntegerParam(s64 val, bool hex = false, int index = 0) : val(val), hex(hex), index(index) {}
|
|
|
|
|
2020-10-09 13:24:55 -04:00
|
|
|
// This is used to generate the value that is passed into the template engine
|
|
|
|
// and injected into the file of lisp code.
|
|
|
|
// In most cases this will probably be a string but look into inja's capabilities
|
|
|
|
// - https://github.com/pantor/inja
|
2020-10-03 18:32:01 -04:00
|
|
|
std::string toLisp() {
|
|
|
|
// Append hex reader macro '#x'
|
|
|
|
if (hex) {
|
|
|
|
return std::string("#x") + std::string(std::to_string(val));
|
|
|
|
}
|
|
|
|
return std::to_string(val);
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:24:55 -04:00
|
|
|
// This is used by the test runner code to know what the expected value is
|
|
|
|
// For a simple example like this, a single eval is all that's required, but for
|
|
|
|
// more complex tests, this may not be the case.
|
2020-10-03 18:32:01 -04:00
|
|
|
std::string eval() {
|
|
|
|
if (hex) {
|
|
|
|
int64_t hexVal;
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << std::hex << std::to_string(val);
|
|
|
|
ss >> hexVal;
|
|
|
|
return std::string(std::to_string(hexVal)) + "\n";
|
|
|
|
}
|
|
|
|
return std::to_string(val) + "\n";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// This is a helper function that is used to generate a bunch of tests
|
|
|
|
// once again, very over-engineered for just testing engineers, but you might imagine
|
|
|
|
// a more complex test template that has several conditionals / loops / etc
|
|
|
|
std::vector<IntegerParam> genIntegerTests(int numTests,
|
|
|
|
std::vector<IntegerParam> additionalTests = {}) {
|
|
|
|
std::vector<IntegerParam> tests;
|
|
|
|
std::random_device dev;
|
|
|
|
std::mt19937 rng(dev());
|
|
|
|
std::uniform_int_distribution<std::mt19937::result_type> dist6(0, UINT32_MAX);
|
2020-10-09 20:33:35 -04:00
|
|
|
int testCases = 3;
|
2020-10-03 18:32:01 -04:00
|
|
|
for (int i = 0; i < numTests; i++) {
|
|
|
|
switch (i % testCases) {
|
|
|
|
case 0:
|
|
|
|
tests.push_back(IntegerParam(dist6(rng), false, i));
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
tests.push_back(IntegerParam(dist6(rng) * -1, false, i));
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
tests.push_back(IntegerParam(dist6(rng), true, i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < additionalTests.size(); i++) {
|
|
|
|
IntegerParam test = additionalTests.at(i);
|
|
|
|
test.index = i + numTests - 1;
|
|
|
|
tests.push_back(test);
|
|
|
|
}
|
|
|
|
|
|
|
|
return tests;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In the interest of speed, we want to share the same thread/compiler across
|
|
|
|
// all the tests in this suite, so we have to over-ride this.
|
2020-10-08 00:06:48 -04:00
|
|
|
class ArithmeticTests : public testing::TestWithParam<IntegerParam> {
|
2020-10-03 18:32:01 -04:00
|
|
|
public:
|
|
|
|
// Per-test-suite set-up.
|
|
|
|
// Called before the first test in this test suite.
|
|
|
|
static void SetUpTestSuite() {
|
|
|
|
runtime_thread = std::thread((GoalTest::runtime_no_kernel));
|
|
|
|
runner.c = &compiler;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Per-test-suite tear-down.
|
|
|
|
// Called after the last test in this test suite.
|
|
|
|
static void TearDownTestSuite() {
|
|
|
|
compiler.shutdown_target();
|
|
|
|
runtime_thread.join();
|
|
|
|
}
|
|
|
|
|
|
|
|
// You can define per-test set-up logic as usual.
|
2020-10-08 00:06:48 -04:00
|
|
|
void SetUp() {
|
|
|
|
GoalTest::createDirIfAbsent(GoalTest::getTemplateDir(testCategory));
|
|
|
|
GoalTest::createDirIfAbsent(GoalTest::getGeneratedDir(testCategory));
|
|
|
|
}
|
2020-10-03 18:32:01 -04:00
|
|
|
|
|
|
|
// You can define per-test tear-down logic as usual.
|
2020-10-08 00:06:48 -04:00
|
|
|
void TearDown() {}
|
2020-10-03 18:32:01 -04:00
|
|
|
|
|
|
|
// Common Resources Across all Tests in the Suite
|
|
|
|
static std::thread runtime_thread;
|
|
|
|
static Compiler compiler;
|
|
|
|
static GoalTest::CompilerTestRunner runner;
|
2020-10-08 00:06:48 -04:00
|
|
|
|
2020-10-09 13:24:55 -04:00
|
|
|
// Just to promote better test organization, supports nesting the test files 1 directory deep
|
|
|
|
std::string testCategory = "arithmetic";
|
|
|
|
inja::Environment env{GoalTest::getTemplateDir(testCategory),
|
|
|
|
GoalTest::getGeneratedDir(testCategory)};
|
2020-10-03 18:32:01 -04:00
|
|
|
};
|
|
|
|
|
2020-10-09 13:24:55 -04:00
|
|
|
// You must initialize the static variables outside of the declaration, or you'll run into
|
|
|
|
// unresolved external errors
|
2020-10-08 00:06:48 -04:00
|
|
|
std::thread ArithmeticTests::runtime_thread;
|
|
|
|
Compiler ArithmeticTests::compiler;
|
|
|
|
GoalTest::CompilerTestRunner ArithmeticTests::runner;
|
2020-10-03 18:32:01 -04:00
|
|
|
|
|
|
|
// Finally, we define our generic test, given our custom class that represents our test inputs
|
|
|
|
// we can generate the lisp file, and pass along the path to the test runner
|
2020-10-09 13:24:55 -04:00
|
|
|
// If the test fails, the test runner will save the template file, with the expected/actual results
|
|
|
|
// into the `failed/` directory
|
2020-10-08 00:06:48 -04:00
|
|
|
TEST_P(ArithmeticTests, EvalIntegers) {
|
2020-10-03 18:32:01 -04:00
|
|
|
IntegerParam param = GetParam();
|
|
|
|
nlohmann::json data;
|
|
|
|
data["integer"] = param.toLisp();
|
|
|
|
|
2020-10-08 00:06:48 -04:00
|
|
|
std::string testFile = "eval-integer-" + std::to_string(param.index) + ".generated.gc";
|
|
|
|
env.write("eval-integer.template.gc", data, testFile);
|
|
|
|
runner.run_test(testCategory, testFile, {param.eval()});
|
2020-10-03 18:32:01 -04:00
|
|
|
}
|
|
|
|
|
2020-10-09 13:24:55 -04:00
|
|
|
// ValuesIn, is not the only way to use a parameterized test, but the most applicable for this
|
|
|
|
// example You can actually get googletest to compute the permutations for you, which may be useful.
|
|
|
|
// Consult their docs.
|
|
|
|
// -
|
|
|
|
// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#value-parameterized-tests
|
2020-10-08 00:06:48 -04:00
|
|
|
INSTANTIATE_TEST_SUITE_P(EvalIntegers,
|
|
|
|
ArithmeticTests,
|
2020-10-09 20:33:35 -04:00
|
|
|
testing::ValuesIn(genIntegerTests(3,
|
2020-10-03 18:32:01 -04:00
|
|
|
{IntegerParam(-2147483648),
|
|
|
|
IntegerParam(0), IntegerParam(-0)})));
|
2020-10-08 00:06:48 -04:00
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, Addition) {
|
|
|
|
runner.run_static_test(env, testCategory, "add-int-literals.static.gc", {"13\n"});
|
2020-10-09 13:24:55 -04:00
|
|
|
runner.run_static_test(env, testCategory, "add-let.static.gc", {"7\n"});
|
2020-10-08 00:06:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, AddIntegerFunction) {
|
|
|
|
runner.run_static_test(env, testCategory, "add-function.static.gc", {"21\n"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, AddIntegerMultiple) {
|
|
|
|
runner.run_static_test(env, testCategory, "add-int-multiple.static.gc", {"15\n"});
|
2020-10-09 13:24:55 -04:00
|
|
|
runner.run_static_test(env, testCategory, "add-int-multiple-2.static.gc", {"15\n"});
|
2020-10-08 00:06:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, AddIntegerVariables) {
|
|
|
|
runner.run_static_test(env, testCategory, "add-int-vars.static.gc", {"7\n"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, AshFunction) {
|
|
|
|
runner.run_static_test(env, testCategory, "ash.static.gc", {"18\n"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, Division) {
|
|
|
|
runner.run_static_test(env, testCategory, "divide-1.static.gc", {"6\n"});
|
2020-10-09 13:24:55 -04:00
|
|
|
runner.run_static_test(env, testCategory, "divide-2.static.gc", {"7\n"});
|
2020-10-08 00:06:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, IntegerSymbol) {
|
|
|
|
runner.run_static_test(env, testCategory, "negative-int-symbol.static.gc", {"-123\n"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, Modulus) {
|
|
|
|
runner.run_static_test(env, testCategory, "mod.static.gc", {"7\n"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, Multiplication) {
|
|
|
|
runner.run_static_test(env, testCategory, "multiply.static.gc", {"-12\n"});
|
2020-10-09 13:24:55 -04:00
|
|
|
runner.run_static_test(env, testCategory, "multiply-let.static.gc", {"3\n"});
|
2020-10-08 00:06:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, NestedFunctionCall) {
|
|
|
|
runner.run_static_test(env, testCategory, "nested-function.static.gc", {"10\n"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, ShiftOperations) {
|
|
|
|
runner.run_static_test(env, testCategory, "shiftvs.static.gc", {"11\n"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ArithmeticTests, Subtraction) {
|
|
|
|
runner.run_static_test(env, testCategory, "subtract-1.static.gc", {"4\n"});
|
2020-10-09 13:24:55 -04:00
|
|
|
runner.run_static_test(env, testCategory, "subtract-2.static.gc", {"4\n"});
|
|
|
|
runner.run_static_test(env, testCategory, "subtract-let.static.gc", {"3\n"});
|
2020-10-08 00:06:48 -04:00
|
|
|
}
|