2023-11-10 21:03:03 +00:00
|
|
|
// See License Information at the bottom of this file
|
|
|
|
|
2023-11-10 20:47:11 +00:00
|
|
|
#import "Basic";
|
|
|
|
#import "Compiler";
|
|
|
|
#import "File";
|
|
|
|
#import "File_Utilities";
|
|
|
|
#import "String";
|
|
|
|
|
|
|
|
Test_Category :: struct
|
|
|
|
{
|
|
|
|
file_extension: string = ".test.jai";
|
|
|
|
// The extension all test files will end in.
|
|
|
|
// You can override this, but it is recommended that you never set it to .jai as
|
|
|
|
// that will match all your normal jai files as well.
|
|
|
|
|
|
|
|
exe_extension: string = "";
|
|
|
|
// The extension of the output test executable.
|
|
|
|
// If not set, this will be overridden
|
|
|
|
// with .test.exe on windows and .test everywhere else.
|
|
|
|
|
|
|
|
category_name: string = "default";
|
|
|
|
// If set to anything other than defualt, will put these tests inside an if block inside
|
|
|
|
// run_all_tests that checks for this name in the arguments to run_all_tests.sh
|
|
|
|
// ie. if category_name is "unit", then calling "run_all_tests.sh unit" will run just the
|
|
|
|
// tests in this category
|
|
|
|
|
|
|
|
exclude_from_run_all_tests: bool = false;
|
|
|
|
// This is useful if you have a category of tests that need to be run manually, or don't
|
|
|
|
// make sense to be run in CI
|
|
|
|
}
|
|
|
|
|
|
|
|
Config :: struct
|
|
|
|
{
|
|
|
|
root_paths: []string;
|
|
|
|
// REQUIRED: The root directory in which tests are found
|
|
|
|
|
|
|
|
test_categories: []Test_Category;
|
|
|
|
// REQUIRED: You must supply at least one test category. However, it is enough to simply
|
|
|
|
// do:
|
|
|
|
// config.test_categories = .[.{}];
|
|
|
|
|
|
|
|
tests_output_dir: string = "";
|
|
|
|
// Where test executables will be output.
|
|
|
|
// If not set, this will be set to the value of {build_options.root_path}/tests_out
|
|
|
|
}
|
|
|
|
|
|
|
|
build_all_tests :: (config: Config, build_options: Build_Options, loc := #caller_location)
|
|
|
|
{
|
|
|
|
set_optimization(*build_options, .DEBUG);
|
|
|
|
configure(config, build_options, loc);
|
|
|
|
output();
|
|
|
|
}
|
|
|
|
|
|
|
|
configure :: (config: Config, build_options: Build_Options, loc := #caller_location)
|
|
|
|
{
|
|
|
|
stored_config = config;
|
|
|
|
|
|
|
|
if stored_config.test_categories.count == 0
|
|
|
|
{
|
|
|
|
print("gs_test: Error - No test_categories provided, so no tests will be build. Aborting.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for * stored_config.test_categories
|
|
|
|
{
|
|
|
|
if (it.file_extension == "") it.file_extension = ".test.jai";
|
|
|
|
if (it.exe_extension == "")
|
|
|
|
{
|
|
|
|
it.exe_extension = ".test";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stored_config.root_paths.count == 0)
|
|
|
|
{
|
|
|
|
compiler_report(loc.fully_pathed_filename, loc.line_number, loc.character_number, "You must supply a root_path in gs_test's config object");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stored_config.tests_output_dir == "")
|
|
|
|
{
|
|
|
|
path := parse_path(stored_config.root_paths[0]);
|
|
|
|
array_add(*path.words, "tests_out");
|
|
|
|
stored_config.tests_output_dir = path_to_string(path);
|
|
|
|
print("Fallback tests_output_dir: %\n", stored_config.tests_output_dir);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for i: 0..stored_config.tests_output_dir.count - 1
|
|
|
|
{
|
|
|
|
if stored_config.tests_output_dir[i] == #char "\\"
|
|
|
|
{
|
|
|
|
stored_config.tests_output_dir[i] = #char "/";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
path := parse_path(stored_config.tests_output_dir);
|
|
|
|
stored_config.tests_output_dir = path_to_string(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
stored_build_options = build_options;
|
|
|
|
stored_build_options.output_path = stored_config.tests_output_dir;
|
|
|
|
|
|
|
|
make_directory_if_it_does_not_exist(stored_build_options.output_path, true);
|
|
|
|
|
|
|
|
for root_path: config.root_paths
|
|
|
|
{
|
|
|
|
visit_files(root_path, true, *test_files, record_test_file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
output :: ()
|
|
|
|
{
|
|
|
|
run_all_tests_builder: String_Builder;
|
|
|
|
init_string_builder(*run_all_tests_builder);
|
|
|
|
append(*run_all_tests_builder, "#!/bin/bash\n");
|
|
|
|
append(*run_all_tests_builder, "SUCCESS=0\n");
|
|
|
|
append(*run_all_tests_builder, "ARGS=$@\n");
|
|
|
|
|
|
|
|
category_builders: [..]String_Builder;
|
|
|
|
for stored_config.test_categories
|
|
|
|
{
|
|
|
|
b := array_add(*category_builders);
|
|
|
|
init_string_builder(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
for test_files
|
|
|
|
{
|
|
|
|
category := stored_config.test_categories[it.category];
|
|
|
|
exe_path := build_test_file(it.file_path, category);
|
|
|
|
if (exe_path != "" && !category.exclude_from_run_all_tests)
|
|
|
|
{
|
|
|
|
print_to_builder(*category_builders[it.category], "% $@\n%", exe_path, UPDATE_SUCCESS_CODE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for category, i: stored_config.test_categories
|
|
|
|
{
|
|
|
|
if category.exclude_from_run_all_tests continue;
|
|
|
|
|
|
|
|
category_builder := *category_builders[i];
|
|
|
|
print_to_builder(*run_all_tests_builder, "\n# Category: %\n", category.category_name);
|
|
|
|
if category.category_name != "default"
|
|
|
|
{
|
|
|
|
print_to_builder(*run_all_tests_builder, "if [[ \" ${ARGS[*]} \" =~ \" % \" ]]; then\n", category.category_name);
|
|
|
|
}
|
|
|
|
print_to_builder(*run_all_tests_builder, "%\n", builder_to_string(category_builder));
|
|
|
|
if category.category_name != "default"
|
|
|
|
{
|
|
|
|
print_to_builder(*run_all_tests_builder, "fi\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
append(*run_all_tests_builder, "exit $SUCCESS");
|
|
|
|
run_all_tests_sh := builder_to_string(*run_all_tests_builder);
|
|
|
|
write_entire_file(string_append(stored_config.tests_output_dir, "/run_all_tests.sh"), run_all_tests_sh);
|
|
|
|
}
|
|
|
|
|
|
|
|
#scope_file
|
|
|
|
|
|
|
|
stored_config: Config;
|
|
|
|
stored_build_options: Build_Options;
|
|
|
|
|
|
|
|
Test_File :: struct
|
|
|
|
{
|
|
|
|
file_path: string;
|
|
|
|
category: int;
|
|
|
|
}
|
|
|
|
|
|
|
|
test_files: [..]Test_File;
|
|
|
|
|
|
|
|
UPDATE_SUCCESS_CODE :: #string DONE
|
|
|
|
if [ $? != 0 ];
|
|
|
|
then
|
|
|
|
SUCCESS=1
|
|
|
|
fi
|
|
|
|
DONE
|
|
|
|
|
|
|
|
string_append :: (args: .. string) -> string
|
|
|
|
{
|
|
|
|
sb: String_Builder;
|
|
|
|
init_string_builder(*sb);
|
|
|
|
for args {
|
|
|
|
append(*sb, it);
|
|
|
|
}
|
|
|
|
return builder_to_string(*sb);
|
|
|
|
}
|
|
|
|
|
|
|
|
record_test_file :: (info: *File_Visit_Info, test_files: *[..]Test_File)
|
|
|
|
{
|
|
|
|
if (info.is_directory) return;
|
|
|
|
|
|
|
|
longest_match := 0;
|
|
|
|
longest_match_index := -1;
|
|
|
|
for category, category_i: stored_config.test_categories
|
|
|
|
{
|
|
|
|
if (ends_with_nocase(info.full_name, category.file_extension))
|
|
|
|
{
|
|
|
|
if longest_match < category.file_extension.count
|
|
|
|
{
|
|
|
|
longest_match = category.file_extension.count;
|
|
|
|
longest_match_index = category_i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if longest_match_index >= 0
|
|
|
|
{
|
|
|
|
array_add(test_files, .{
|
|
|
|
file_path = copy_string(info.full_name),
|
|
|
|
category = longest_match_index
|
|
|
|
});
|
|
|
|
print(" Test File: % - Category: %\n", info.full_name, stored_config.test_categories[longest_match_index].category_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
build_test_file :: (path_string: string, category: Test_Category) -> string
|
|
|
|
{
|
|
|
|
workspace := compiler_create_workspace(tprint("Test: %", path_string));
|
|
|
|
if !workspace
|
|
|
|
{
|
|
|
|
compiler_report(path_string, 0, 0, "Unable to create workspace for test file");
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
path := parse_path(path_string);
|
|
|
|
filename := path.words[path.words.count - 1];
|
|
|
|
filename_without_ext := slice(filename, 0, filename.count - category.file_extension.count);
|
|
|
|
exe_output_path := string_append(
|
|
|
|
filename_without_ext,
|
|
|
|
category.exe_extension
|
|
|
|
);
|
|
|
|
|
|
|
|
build_options := stored_build_options;
|
|
|
|
build_options.output_executable_name = exe_output_path;
|
|
|
|
|
|
|
|
set_build_options(build_options, workspace);
|
|
|
|
add_build_file(path_string, workspace);
|
|
|
|
add_build_string(tprint("#load \"%harness.jai\";\n", #filepath), workspace);
|
|
|
|
|
|
|
|
output_path := parse_path(stored_build_options.output_path);
|
|
|
|
array_add(*output_path.words, exe_output_path);
|
|
|
|
output_path.trailing_slash = false;
|
|
|
|
|
|
|
|
return path_to_string(output_path);
|
2023-11-10 21:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
zlib License
|
|
|
|
|
|
|
|
(C) Copyright 2023 Peter Slattery
|
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any damages
|
|
|
|
arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
|
|
including commercial applications, and to alter it and redistribute it
|
|
|
|
freely, subject to the following restrictions:
|
|
|
|
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
|
|
claim that you wrote the original software. If you use this software
|
|
|
|
in a product, an acknowledgment in the product documentation would be
|
|
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
|
|
|
|
*/
|