Initial commit.

This commit is contained in:
2021-08-22 19:03:03 +02:00
commit a6013437b2
17 changed files with 27667 additions and 0 deletions

1
src/BuildData Submodule

Submodule src/BuildData added at 069eadf7c9

6
src/Precompiled.hxx Normal file
View File

@@ -0,0 +1,6 @@
#ifndef POKEMONSCRIPTTESTER_PRECOMPILED_HXX
#define POKEMONSCRIPTTESTER_PRECOMPILED_HXX
#include <PkmnLib/Precompiled.hxx>
#endif // POKEMONSCRIPTTESTER_PRECOMPILED_HXX

View File

@@ -0,0 +1,48 @@
#ifndef POKEMONSCRIPTTESTER_ASSERTIONFAILED_HPP
#define POKEMONSCRIPTTESTER_ASSERTIONFAILED_HPP
#include <exception>
#include <sstream>
class AssertionFailed : public std::exception {
static inline std::string& ltrim(std::string& s, const char* t = " \t\n\r\f\v") {
s.erase(0, s.find_first_not_of(t));
return s;
}
static std::string GetLine(const std::string& path, size_t num) {
std::ifstream file;
file.open(path);
file.seekg(std::ios::beg);
for (size_t i = 0; i < num - 1; ++i) {
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
std::string line;
std::getline(file, line);
file.close();
line = ltrim(line);
return line;
}
char* _msg;
public:
AssertionFailed(const std::string& path, size_t line, const std::string& message = "") {
auto l = GetLine(path, line);
std::stringstream ss;
ss << "Assertion failed at: " << path << ":" << line << std::endl;
ss << l;
if (message.length() > 0) {
ss << std::endl << message;
}
auto str = ss.str();
_msg = new char[str.length() + 1];
str.copy(_msg, str.length(), 0);
_msg[str.length()] = 0;
}
~AssertionFailed() { delete[] _msg; }
const char* what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW override { return _msg; }
};
#endif // POKEMONSCRIPTTESTER_ASSERTIONFAILED_HPP

40
src/Tester/Test.hpp Normal file
View File

@@ -0,0 +1,40 @@
#ifndef POKEMONSCRIPTTESTER_TEST_HPP
#define POKEMONSCRIPTTESTER_TEST_HPP
#include <angelscript.h>
#include "TestEnvironment.hpp"
#include "TestResult.hpp"
class Test {
public:
Test(const std::string name, asIScriptFunction* function)
: _name(name), _function(function), _env(new TestEnvironment()) {}
void Run(asIScriptContext* ctx) {
ctx->Prepare(_function);
ctx->SetUserData(_env);
auto e = ctx->Execute();
if (e == asEXECUTION_EXCEPTION) {
_errorMessage = ctx->GetExceptionString();
_result = TestResult::Failed;
ctx->Release();
return;
}
ctx->Release();
Ensure(e == asEXECUTION_FINISHED);
_result = TestResult::Success;
}
inline TestResult GetResult() const noexcept { return _result; }
inline const std::string& GetErrorMessage() const noexcept { return _errorMessage; }
private:
std::string _name;
asIScriptFunction* _function;
TestEnvironment* _env;
TestResult _result = TestResult::NotRan;
std::string _errorMessage = "";
};
#endif // POKEMONSCRIPTTESTER_TEST_HPP

View File

@@ -0,0 +1,8 @@
#ifndef POKEMONSCRIPTTESTER_TESTENVIRONMENT_HPP
#define POKEMONSCRIPTTESTER_TESTENVIRONMENT_HPP
struct TestEnvironment{
size_t AssertionCount = 0;
};
#endif // POKEMONSCRIPTTESTER_TESTENVIRONMENT_HPP

View File

@@ -0,0 +1,61 @@
#ifndef POKEMONSCRIPTTESTER_TESTFUNCTIONS_HPP
#define POKEMONSCRIPTTESTER_TESTFUNCTIONS_HPP
#include <angelscript.h>
#include "AssertionFailed.hpp"
#include "TestEnvironment.hpp"
class TestFunctions {
private:
struct AssertionData {
std::string FileName;
size_t Line;
};
static AssertionData GetAssertionData() {
auto* ctx = asGetActiveContext();
TestEnvironment* env = static_cast<TestEnvironment*>(ctx->GetUserData());
env->AssertionCount += 1;
auto filename = ctx->GetFunction()->GetScriptSectionName();
auto line = ctx->GetLineNumber();
return AssertionData{
.FileName = filename,
.Line = (size_t)line,
};
}
static bool Assert(bool value) {
if (!value) {
auto data = GetAssertionData();
throw AssertionFailed(data.FileName, data.Line);
}
return true;
}
#define GetEqualityMessage(expected, actual) \
std::string eqMsg; \
{ \
std::ostringstream ss; \
ss << "Expected: '" << std::to_string(expected) << "', but was: '" << std::to_string(actual) << "'"; \
eqMsg = ss.str(); \
}
static bool AssertEquals(i32 expected, i32 actual) {
if (expected != actual) {
auto data = GetAssertionData();
GetEqualityMessage(expected, actual);
throw AssertionFailed(data.FileName, data.Line, eqMsg);
}
return true;
}
public:
static void Register(AngelScriptResolver* scriptResolver) {
auto engine = scriptResolver->GetBuilder().GetEngine();
Ensure(engine->RegisterGlobalFunction("bool Assert(bool expression)", asFUNCTION(Assert), asCALL_CDECL) >= 0);
Ensure(engine->RegisterGlobalFunction("bool AssertEquals(int expected, int actual)", asFUNCTION(AssertEquals),
asCALL_CDECL) >= 0);
}
};
#endif // POKEMONSCRIPTTESTER_TESTFUNCTIONS_HPP

View File

@@ -0,0 +1,6 @@
#ifndef POKEMONSCRIPTTESTER_TESTRESULT_HPP
#define POKEMONSCRIPTTESTER_TESTRESULT_HPP
ENUM(TestResult, u8, NotRan, Success, Failed)
#endif // POKEMONSCRIPTTESTER_TESTRESULT_HPP

42
src/Tester/TestRunner.hpp Normal file
View File

@@ -0,0 +1,42 @@
#ifndef POKEMONSCRIPTTESTER_TESTRUNNER_HPP
#define POKEMONSCRIPTTESTER_TESTRUNNER_HPP
#include <PkmnLib/ScriptResolving/AngelScript/AngelScriptMetadata.hpp>
#include "Test.hpp"
class TestRunner {
ArbUt::Dictionary<std::string, Test> _tests;
public:
TestRunner(AngelScriptResolver* scriptResolver) {
const auto* module = scriptResolver->GetMainModule();
auto builder = scriptResolver->GetBuilder();
for (u32 i = 0; i < module->GetFunctionCount(); ++i) {
auto* func = module->GetFunctionByIndex(i);
auto metaData = builder.GetMetadataForFunc(func);
for (const auto& m : metaData) {
auto meta = AngelscriptMetadata(m);
if (meta.GetIdentifier() == "Test"_cnc) {
auto name = meta.GetParameter("name"_cnc);
_tests.Insert(name, Test(name, func));
}
}
}
}
void RunAll(asIScriptEngine* engine) {
auto ctx = engine->CreateContext();
for (auto& test : _tests) {
test.second.Run(ctx);
}
for (auto& test : _tests) {
if (test.second.GetResult() == TestResult::Failed) {
std::cout << "Test '" << test.first << "' failed with message: " << std::endl;
std::cout << test.second.GetErrorMessage() << std::endl << std::endl;
}
}
}
};
#endif // POKEMONSCRIPTTESTER_TESTRUNNER_HPP

56
src/main.cpp Normal file
View File

@@ -0,0 +1,56 @@
#include <PkmnLib/ScriptResolving/AngelScript/AngelScriptResolver.hpp>
#include <filesystem>
#include <iostream>
#include "../extern/args.hpp"
#include "BuildData/BuildLibrary.hpp"
#include "Tester/TestFunctions.hpp"
#include "Tester/TestRunner.hpp"
int main(int argc, char** argv) {
args::ArgumentParser parser("PkmnLib Script Tester.", "");
args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"});
std::string workingDirectory;
args::HelpFlag helpFlag(parser, "help", "Display this help menu", {'h', "help"});
args::ValueFlag<std::string> workingDirFlag(parser, "Working Directory", "Which work directory to use.",
{"workdir"});
try {
parser.ParseCLI(argc, argv);
} catch (args::Help&) {
std::cout << parser;
return 0;
} catch (args::ParseError& e) {
std::cerr << e.what() << std::endl;
std::cerr << parser;
return 1;
} catch (args::ValidationError& e) {
std::cerr << e.what() << std::endl;
std::cerr << parser;
return 1;
}
if (workingDirFlag) {
workingDirectory = args::get(workingDirFlag);
}
if (!workingDirectory.empty()) {
chdir(std::filesystem::path(workingDirectory).c_str());
}
std::function<void(PkmnLib::Battling::ScriptResolver*)> initialize =
[](PkmnLib::Battling::ScriptResolver* resolver) {
auto* scriptResolver = dynamic_cast<AngelScriptResolver*>(resolver);
scriptResolver->DefineWord("TEST");
TestFunctions::Register(scriptResolver);
};
auto battleLib = ArbUt::OptionalScopedPtr(BuildLibrary::Build("", initialize));
if (!battleLib.HasValue()) {
return 1;
}
auto* scriptResolver = dynamic_cast<AngelScriptResolver*>(battleLib.GetValue()->GetScriptResolver().get());
auto testRunner = TestRunner(scriptResolver);
auto engine = scriptResolver->GetBuilder().GetEngine();
testRunner.RunAll(engine);
return 0;
}