Initial commit.
This commit is contained in:
1
src/BuildData
Submodule
1
src/BuildData
Submodule
Submodule src/BuildData added at 069eadf7c9
6
src/Precompiled.hxx
Normal file
6
src/Precompiled.hxx
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef POKEMONSCRIPTTESTER_PRECOMPILED_HXX
|
||||
#define POKEMONSCRIPTTESTER_PRECOMPILED_HXX
|
||||
|
||||
#include <PkmnLib/Precompiled.hxx>
|
||||
|
||||
#endif // POKEMONSCRIPTTESTER_PRECOMPILED_HXX
|
||||
48
src/Tester/AssertionFailed.hpp
Normal file
48
src/Tester/AssertionFailed.hpp
Normal 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
40
src/Tester/Test.hpp
Normal 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
|
||||
8
src/Tester/TestEnvironment.hpp
Normal file
8
src/Tester/TestEnvironment.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef POKEMONSCRIPTTESTER_TESTENVIRONMENT_HPP
|
||||
#define POKEMONSCRIPTTESTER_TESTENVIRONMENT_HPP
|
||||
|
||||
struct TestEnvironment{
|
||||
size_t AssertionCount = 0;
|
||||
};
|
||||
|
||||
#endif // POKEMONSCRIPTTESTER_TESTENVIRONMENT_HPP
|
||||
61
src/Tester/TestFunctions.hpp
Normal file
61
src/Tester/TestFunctions.hpp
Normal 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
|
||||
6
src/Tester/TestResult.hpp
Normal file
6
src/Tester/TestResult.hpp
Normal 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
42
src/Tester/TestRunner.hpp
Normal 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
56
src/main.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user