add vctest (WIP)
This commit is contained in:
parent
d9bd60f473
commit
5e8805f241
1
.github/workflows/appimage.yml
vendored
1
.github/workflows/appimage.yml
vendored
@ -41,6 +41,7 @@ jobs:
|
||||
- name: Run tests
|
||||
run: ctest --test-dir ${{github.workspace}}/build
|
||||
- name: Run engine (headless)
|
||||
timeout-minutes: 1
|
||||
run: |
|
||||
chmod +x ${{github.workspace}}/build/VoxelEngine
|
||||
${{github.workspace}}/build/VoxelEngine --headless --dir ${{github.workspace}}/userdir
|
||||
|
||||
1
.github/workflows/macos.yml
vendored
1
.github/workflows/macos.yml
vendored
@ -43,6 +43,7 @@ jobs:
|
||||
run: |
|
||||
chmod +x build/VoxelEngine
|
||||
build/VoxelEngine --headless --dir userdir
|
||||
timeout-minutes: 1
|
||||
# - name: Create DMG
|
||||
# run: |
|
||||
# mkdir VoxelEngineDmgContent
|
||||
|
||||
1
.github/workflows/windows.yml
vendored
1
.github/workflows/windows.yml
vendored
@ -38,6 +38,7 @@ jobs:
|
||||
run: ctest --output-on-failure --test-dir build
|
||||
- name: Run engine (headless)
|
||||
run: build/Release/VoxelEngine.exe --headless --dir userdir
|
||||
timeout-minutes: 1
|
||||
# - name: Package for Windows
|
||||
# run: |
|
||||
# mkdir packaged
|
||||
|
||||
@ -82,3 +82,5 @@ if (VOXELENGINE_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
add_subdirectory(vctest)
|
||||
|
||||
1
dev/tests/example.lua
Normal file
1
dev/tests/example.lua
Normal file
@ -0,0 +1 @@
|
||||
print("Hello from the example test!")
|
||||
@ -328,6 +328,43 @@ function __vc_on_world_quit()
|
||||
_rules.clear()
|
||||
end
|
||||
|
||||
local __vc_coroutines = {}
|
||||
local __vc_next_coroutine = 1
|
||||
local __vc_coroutine_error = nil
|
||||
|
||||
function __vc_start_coroutine(chunk)
|
||||
local co = coroutine.create(function()
|
||||
local status, err = pcall(chunk)
|
||||
if not status then
|
||||
__vc_coroutine_error = err
|
||||
end
|
||||
end)
|
||||
local id = __vc_next_coroutine
|
||||
__vc_next_coroutine = __vc_next_coroutine + 1
|
||||
__vc_coroutines[id] = co
|
||||
return id
|
||||
end
|
||||
|
||||
function __vc_resume_coroutine(id)
|
||||
local co = __vc_coroutines[id]
|
||||
if co then
|
||||
coroutine.resume(co)
|
||||
if __vc_coroutine_error then
|
||||
error(__vc_coroutine_error)
|
||||
end
|
||||
return coroutine.status(co) ~= "dead"
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function __vc_stop_coroutine(id)
|
||||
local co = __vc_coroutines[id]
|
||||
if co then
|
||||
coroutine.close(co)
|
||||
__vc_coroutines[id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
assets = {}
|
||||
assets.load_texture = core.__load_texture
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
#include "window/Events.hpp"
|
||||
#include "window/input.hpp"
|
||||
#include "window/Window.hpp"
|
||||
#include "interfaces/Process.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
@ -178,12 +179,25 @@ void Engine::saveScreenshot() {
|
||||
|
||||
void Engine::run() {
|
||||
if (params.headless) {
|
||||
logger.info() << "nothing to do";
|
||||
runTest();
|
||||
} else {
|
||||
mainloop();
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::runTest() {
|
||||
if (params.testFile.empty()) {
|
||||
logger.info() << "nothing to do";
|
||||
return;
|
||||
}
|
||||
logger.info() << "starting test " << params.testFile;
|
||||
auto process = scripting::start_coroutine(params.testFile);
|
||||
while (process->isActive()) {
|
||||
process->update();
|
||||
}
|
||||
logger.info() << "test finished";
|
||||
}
|
||||
|
||||
void Engine::mainloop() {
|
||||
logger.info() << "starting menu screen";
|
||||
setScreen(std::make_shared<MenuScreen>(this));
|
||||
|
||||
@ -49,6 +49,7 @@ struct CoreParameters {
|
||||
bool headless = false;
|
||||
std::filesystem::path resFolder {"res"};
|
||||
std::filesystem::path userFolder {"."};
|
||||
std::filesystem::path testFile;
|
||||
};
|
||||
|
||||
class Engine : public util::ObjectsKeeper {
|
||||
@ -93,6 +94,8 @@ public:
|
||||
/// Automatically sets MenuScreen
|
||||
void mainloop();
|
||||
|
||||
void runTest();
|
||||
|
||||
/// @brief Called after assets loading when all engine systems are initialized
|
||||
void onAssetsLoaded();
|
||||
|
||||
|
||||
12
src/interfaces/Process.hpp
Normal file
12
src/interfaces/Process.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Process interface.
|
||||
class Process {
|
||||
public:
|
||||
virtual ~Process() {}
|
||||
|
||||
virtual bool isActive() const = 0;
|
||||
virtual void update() = 0;
|
||||
virtual void waitForEnd() = 0;
|
||||
virtual void terminate() = 0;
|
||||
};
|
||||
@ -25,6 +25,7 @@
|
||||
#include "util/timeutil.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "interfaces/Process.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
@ -71,6 +72,59 @@ void scripting::initialize(Engine* engine) {
|
||||
load_script(fs::path("classes.lua"), true);
|
||||
}
|
||||
|
||||
class LuaCoroutine : public Process {
|
||||
lua::State* L;
|
||||
int id;
|
||||
bool alive = true;
|
||||
public:
|
||||
LuaCoroutine(lua::State* L, int id) : L(L), id(id) {
|
||||
}
|
||||
|
||||
bool isActive() const override {
|
||||
return alive;
|
||||
}
|
||||
|
||||
void update() override {
|
||||
if (lua::getglobal(L, "__vc_resume_coroutine")) {
|
||||
lua::pushinteger(L, id);
|
||||
if (lua::call(L, 1)) {
|
||||
alive = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void waitForEnd() override {
|
||||
while (isActive()) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void terminate() override {
|
||||
if (lua::getglobal(L, "__vc_stop_coroutine")) {
|
||||
lua::pushinteger(L, id);
|
||||
lua::pop(L, lua::call(L, 1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Process> scripting::start_coroutine(
|
||||
const std::filesystem::path& script
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::getglobal(L, "__vc_start_coroutine")) {
|
||||
auto source = files::read_string(script);
|
||||
lua::loadbuffer(L, 0, source, script.filename().u8string());
|
||||
if (lua::call(L, 1)) {
|
||||
int id = lua::tointeger(L, -1);
|
||||
lua::pop(L, 2);
|
||||
return std::make_unique<LuaCoroutine>(L, id);
|
||||
}
|
||||
lua::pop(L);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] scriptenv scripting::get_root_environment() {
|
||||
return std::make_shared<int>(0);
|
||||
}
|
||||
|
||||
@ -11,8 +11,6 @@
|
||||
#include "typedefs.hpp"
|
||||
#include "scripting_functional.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class Engine;
|
||||
class Content;
|
||||
struct ContentPack;
|
||||
@ -34,6 +32,7 @@ class Entity;
|
||||
struct EntityDef;
|
||||
class GeneratorScript;
|
||||
struct GeneratorDef;
|
||||
class Process;
|
||||
|
||||
namespace scripting {
|
||||
extern Engine* engine;
|
||||
@ -60,6 +59,10 @@ namespace scripting {
|
||||
|
||||
void process_post_runnables();
|
||||
|
||||
std::unique_ptr<Process> start_coroutine(
|
||||
const std::filesystem::path& script
|
||||
);
|
||||
|
||||
void on_world_load(LevelController* controller);
|
||||
void on_world_tick();
|
||||
void on_world_save();
|
||||
@ -136,7 +139,7 @@ namespace scripting {
|
||||
void load_content_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName,
|
||||
block_funcs_set& funcsset
|
||||
);
|
||||
@ -150,7 +153,7 @@ namespace scripting {
|
||||
void load_content_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName,
|
||||
item_funcs_set& funcsset
|
||||
);
|
||||
@ -161,13 +164,13 @@ namespace scripting {
|
||||
/// @param fileName script file path using the engine format
|
||||
void load_entity_component(
|
||||
const std::string& name,
|
||||
const fs::path& file,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName
|
||||
);
|
||||
|
||||
std::unique_ptr<GeneratorScript> load_generator(
|
||||
const GeneratorDef& def,
|
||||
const fs::path& file,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& dirPath
|
||||
);
|
||||
|
||||
@ -179,7 +182,7 @@ namespace scripting {
|
||||
void load_world_script(
|
||||
const scriptenv& env,
|
||||
const std::string& packid,
|
||||
const fs::path& file,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName,
|
||||
world_funcs_set& funcsset
|
||||
);
|
||||
@ -193,7 +196,7 @@ namespace scripting {
|
||||
void load_layout_script(
|
||||
const scriptenv& env,
|
||||
const std::string& prefix,
|
||||
const fs::path& file,
|
||||
const std::filesystem::path& file,
|
||||
const std::string& fileName,
|
||||
uidocscript& script
|
||||
);
|
||||
|
||||
14
src/main.cpp
14
src/main.cpp
@ -3,17 +3,23 @@
|
||||
#include "util/command_line.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
static debug::Logger logger("main");
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
debug::Logger::init("latest.log");
|
||||
|
||||
CoreParameters coreParameters;
|
||||
if (!parse_cmdline(argc, argv, coreParameters)) {
|
||||
return EXIT_SUCCESS;
|
||||
try {
|
||||
if (!parse_cmdline(argc, argv, coreParameters)) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
} catch (const std::runtime_error& err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
debug::Logger::init(coreParameters.userFolder.string()+"/latest.log");
|
||||
platform::configure_encoding();
|
||||
try {
|
||||
Engine(std::move(coreParameters)).run();
|
||||
|
||||
37
src/util/ArgsReader.hpp
Normal file
37
src/util/ArgsReader.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
namespace util {
|
||||
class ArgsReader {
|
||||
const char* last = "";
|
||||
char** argv;
|
||||
int argc;
|
||||
int pos = 0;
|
||||
public:
|
||||
ArgsReader(int argc, char** argv) : argv(argv), argc(argc) {
|
||||
}
|
||||
|
||||
void skip() {
|
||||
pos++;
|
||||
}
|
||||
|
||||
bool hasNext() const {
|
||||
return pos < argc && std::strlen(argv[pos]);
|
||||
}
|
||||
|
||||
bool isKeywordArg() const {
|
||||
return last[0] == '-';
|
||||
}
|
||||
|
||||
std::string next() {
|
||||
if (pos >= argc) {
|
||||
throw std::runtime_error("unexpected end");
|
||||
}
|
||||
last = argv[pos];
|
||||
return argv[pos++];
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,48 +1,16 @@
|
||||
#include "command_line.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "files/engine_paths.hpp"
|
||||
#include "util/ArgsReader.hpp"
|
||||
#include "engine.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class ArgsReader {
|
||||
const char* last = "";
|
||||
char** argv;
|
||||
int argc;
|
||||
int pos = 0;
|
||||
public:
|
||||
ArgsReader(int argc, char** argv) : argv(argv), argc(argc) {
|
||||
}
|
||||
|
||||
void skip() {
|
||||
pos++;
|
||||
}
|
||||
|
||||
bool hasNext() const {
|
||||
return pos < argc && strlen(argv[pos]);
|
||||
}
|
||||
|
||||
bool isKeywordArg() const {
|
||||
return last[0] == '-';
|
||||
}
|
||||
|
||||
std::string next() {
|
||||
if (pos >= argc) {
|
||||
throw std::runtime_error("unexpected end");
|
||||
}
|
||||
last = argv[pos];
|
||||
return argv[pos++];
|
||||
}
|
||||
};
|
||||
|
||||
static bool perform_keyword(
|
||||
ArgsReader& reader, const std::string& keyword, CoreParameters& params
|
||||
util::ArgsReader& reader, const std::string& keyword, CoreParameters& params
|
||||
) {
|
||||
if (keyword == "--res") {
|
||||
auto token = reader.next();
|
||||
@ -53,22 +21,25 @@ static bool perform_keyword(
|
||||
} else if (keyword == "--help" || keyword == "-h") {
|
||||
std::cout << "VoxelEngine command-line arguments:\n";
|
||||
std::cout << " --help - show help\n";
|
||||
std::cout << " --res [path] - set resources directory\n";
|
||||
std::cout << " --dir [path] - set userfiles directory\n";
|
||||
std::cout << " --res <path> - set resources directory\n";
|
||||
std::cout << " --dir <path> - set userfiles directory\n";
|
||||
std::cout << " --headless - run in headless mode\n";
|
||||
std::cout << " --test <path> - test script file\n";
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
} else if (keyword == "--headless") {
|
||||
params.headless = true;
|
||||
} else if (keyword == "--test") {
|
||||
auto token = reader.next();
|
||||
params.testFile = fs::u8path(token);
|
||||
} else {
|
||||
std::cerr << "unknown argument " << keyword << std::endl;
|
||||
return false;
|
||||
throw std::runtime_error("unknown argument " + keyword);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_cmdline(int argc, char** argv, CoreParameters& params) {
|
||||
ArgsReader reader(argc, argv);
|
||||
util::ArgsReader reader(argc, argv);
|
||||
reader.skip();
|
||||
while (reader.hasNext()) {
|
||||
std::string token = reader.next();
|
||||
|
||||
31
vctest/CMakeLists.txt
Normal file
31
vctest/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
project(vctest)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||
|
||||
if(MSVC)
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||
endif()
|
||||
if((CMAKE_BUILD_TYPE EQUAL "Release") OR (CMAKE_BUILD_TYPE EQUAL "RelWithDebInfo"))
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Release>:Release>")
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /MT /O2)
|
||||
else()
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE /W4)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra
|
||||
-Wformat-nonliteral -Wcast-align
|
||||
-Wpointer-arith -Wundef
|
||||
-Wwrite-strings -Wno-unused-parameter)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs")
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src ${CMAKE_DL_LIBS})
|
||||
213
vctest/main.cpp
Normal file
213
vctest/main.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "util/ArgsReader.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
inline fs::path TESTING_DIR = fs::u8path(".vctest");
|
||||
|
||||
struct Config {
|
||||
fs::path executable;
|
||||
fs::path directory;
|
||||
fs::path resDir {"res"};
|
||||
fs::path workingDir {"."};
|
||||
bool outputAlways = false;
|
||||
};
|
||||
|
||||
static bool perform_keyword(
|
||||
util::ArgsReader& reader, const std::string& keyword, Config& config
|
||||
) {
|
||||
if (keyword == "--help" || keyword == "-h") {
|
||||
std::cout << "Options\n\n";
|
||||
std::cout << " --help, -h = show help\n";
|
||||
std::cout << " --exe <path>, -e <path> = VoxelCore executable path\n";
|
||||
std::cout << " --tests <path>, -d <path> = tests directory path\n";
|
||||
std::cout << " --res <path>, -r <path> = 'res' directory path\n";
|
||||
std::cout << " --working-dir <path>, -w <path> = user directory path\n";
|
||||
std::cout << " --output-always = always show tests output\n";
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
} else if (keyword == "--exe" || keyword == "-e") {
|
||||
config.executable = fs::path(reader.next());
|
||||
} else if (keyword == "--tests" || keyword == "-d") {
|
||||
config.directory = fs::path(reader.next());
|
||||
} else if (keyword == "--res" || keyword == "-r") {
|
||||
config.resDir = fs::path(reader.next());
|
||||
} else if (keyword == "--user" || keyword == "-u") {
|
||||
config.workingDir = fs::path(reader.next());
|
||||
} else if (keyword == "--output-always") {
|
||||
config.outputAlways = true;
|
||||
} else {
|
||||
std::cerr << "unknown argument " << keyword << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_cmdline(int argc, char** argv, Config& config) {
|
||||
util::ArgsReader reader(argc, argv);
|
||||
while (reader.hasNext()) {
|
||||
std::string token = reader.next();
|
||||
if (reader.isKeywordArg()) {
|
||||
if (!perform_keyword(reader, token, config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool check_dir(const fs::path& dir) {
|
||||
if (!fs::is_directory(dir)) {
|
||||
std::cerr << dir << " is not a directory" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void print_separator(std::ostream& stream) {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
stream << "=";
|
||||
}
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
static bool check_config(const Config& config) {
|
||||
if (!fs::exists(config.executable)) {
|
||||
std::cerr << "file " << config.executable << " not found" << std::endl;
|
||||
return true;
|
||||
}
|
||||
if (!check_dir(config.directory)) {
|
||||
return true;
|
||||
}
|
||||
if (!check_dir(config.resDir)) {
|
||||
return true;
|
||||
}
|
||||
if (!check_dir(config.workingDir)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dump_config(const Config& config) {
|
||||
std::cout << "paths:\n";
|
||||
std::cout << " VoxelCore executable = " << fs::canonical(config.executable).string() << "\n";
|
||||
std::cout << " Tests directory = " << fs::canonical(config.directory).string() << "\n";
|
||||
std::cout << " Resources directory = " << fs::canonical(config.resDir).string() << "\n";
|
||||
std::cout << " Working directory = " << fs::canonical(config.workingDir).string();
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
static void cleanup(const fs::path& workingDir) {
|
||||
auto dir = workingDir / TESTING_DIR;
|
||||
std::cout << "cleaning up " << dir << std::endl;
|
||||
fs::remove_all(dir);
|
||||
}
|
||||
|
||||
static void setup_working_dir(const fs::path& workingDir) {
|
||||
auto dir = workingDir / TESTING_DIR;
|
||||
std::cout << "setting up working directory " << dir << std::endl;
|
||||
if (fs::is_directory(dir)) {
|
||||
cleanup(workingDir);
|
||||
}
|
||||
fs::create_directories(dir);
|
||||
}
|
||||
|
||||
static void display_test_output(const fs::path& path, std::ostream& stream) {
|
||||
stream << "[OUTPUT]" << std::endl;
|
||||
if (fs::exists(path)) {
|
||||
std::ifstream t(path);
|
||||
stream << t.rdbuf();
|
||||
}
|
||||
}
|
||||
|
||||
static bool run_test(const Config& config, const fs::path& path) {
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::milliseconds;
|
||||
|
||||
auto outputFile = config.workingDir / "output.txt";
|
||||
|
||||
auto name = path.stem();
|
||||
std::stringstream ss;
|
||||
ss << config.executable << " --headless";
|
||||
ss << " --test " << path;
|
||||
ss << " --res " << config.resDir;
|
||||
ss << " --dir " << config.workingDir;
|
||||
ss << " >" << (config.workingDir / "output.txt") << " 2>&1";
|
||||
auto command = ss.str();
|
||||
|
||||
print_separator(std::cout);
|
||||
std::cout << "executing test " << name << "\ncommand: " << command << std::endl;
|
||||
|
||||
auto start = high_resolution_clock::now();
|
||||
int code = system(command.c_str());
|
||||
auto testTime =
|
||||
duration_cast<milliseconds>(high_resolution_clock::now() - start)
|
||||
.count();
|
||||
|
||||
if (code) {
|
||||
display_test_output(outputFile, std::cerr);
|
||||
std::cerr << "[FAILED] " << name << " in " << testTime << " ms" << std::endl;
|
||||
fs::remove(outputFile);
|
||||
return false;
|
||||
} else {
|
||||
if (config.outputAlways) {
|
||||
display_test_output(outputFile, std::cout);
|
||||
}
|
||||
std::cout << "[PASSED] " << name << " in " << testTime << " ms" << std::endl;
|
||||
fs::remove(outputFile);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Config config;
|
||||
try {
|
||||
if (!parse_cmdline(argc, argv, config)) {
|
||||
return 0;
|
||||
}
|
||||
} catch (const std::runtime_error& err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
throw;
|
||||
}
|
||||
if (check_config(config)) {
|
||||
return 1;
|
||||
}
|
||||
dump_config(config);
|
||||
|
||||
std::vector<fs::path> tests;
|
||||
std::cout << "scanning for tests" << std::endl;
|
||||
for (const auto& entry : fs::directory_iterator(config.directory)) {
|
||||
auto path = entry.path();
|
||||
if (path.extension().string() != ".lua") {
|
||||
std::cout << " " << entry.path() << " skipped" << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << " " << entry.path() << " enqueued" << std::endl;
|
||||
tests.push_back(path);
|
||||
}
|
||||
|
||||
setup_working_dir(config.workingDir);
|
||||
config.workingDir /= TESTING_DIR;
|
||||
|
||||
size_t passed = 0;
|
||||
std::cout << "running " << tests.size() << " test(s)" << std::endl;
|
||||
for (const auto& path : tests) {
|
||||
passed += run_test(config, path);
|
||||
}
|
||||
print_separator(std::cout);
|
||||
cleanup(config.workingDir);
|
||||
std::cout << std::endl;
|
||||
std::cout << passed << " test(s) passed, " << (tests.size() - passed)
|
||||
<< " test(s) failed" << std::endl;
|
||||
if (passed < tests.size()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user