diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1f389c0c..9ee0d8a18 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -266,6 +266,7 @@ add_library(poly2tri STATIC target_include_directories(poly2tri PUBLIC ${COMMON_INCLUDES}) set(UI_TEST_SOURCES + slic3r.cpp ${GUI_TESTDIR}/testableframe.cpp ${GUI_TESTDIR}/test_harness_gui.cpp ${GUI_TESTDIR}/test_field_checkbox.cpp @@ -281,6 +282,7 @@ set(UI_TEST_SOURCES ${GUI_TESTDIR}/test_preset.cpp ${GUI_TESTDIR}/test_preset_chooser.cpp ${GUI_TESTDIR}/test_misc_ui.cpp + ${GUI_TESTDIR}/test_cli.cpp ) set(SLIC3R_TEST_SOURCES @@ -450,6 +452,7 @@ IF(wxWidgets_FOUND AND OPENGL_FOUND AND Enable_GUI) target_link_libraries(gui_test PUBLIC slic3r_gui libslic3r Catch ${wxWidgets_LIBRARIES} ${LIBSLIC3R_DEPENDS} ${OPENGL_LIBRARIES}) target_include_directories(gui_test PUBLIC cxx_std_14 ${SLIC3R_GUI_INCLUDES} ) target_include_directories(gui_test PUBLIC ${TESTDIR}) + target_compile_options(gui_test PUBLIC -DBUILD_TEST) endif() if (WIN32) target_link_libraries(slic3r uxtheme) diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 880b9fb51..650e6ad02 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -25,10 +25,12 @@ using namespace Slic3r; +#ifndef BUILD_TEST int main(int argc, char **argv) { return CLI().run(argc, argv); } +#endif // BUILD_TEST int CLI::run(int argc, char **argv) { // Convert arguments to UTF-8 (needed on Windows). diff --git a/src/test/GUI/test_cli.cpp b/src/test/GUI/test_cli.cpp new file mode 100644 index 000000000..25621c3e4 --- /dev/null +++ b/src/test/GUI/test_cli.cpp @@ -0,0 +1,193 @@ +#include +#include +#include + +#include "test_options.hpp" + +#include "slic3r.hpp" + +using namespace Slic3r; +using namespace std::string_literals; + +bool file_exists(const std::string& name, const std::string& ext) { + std::string filename {""}; + filename.append(name); + filename.append("."); + filename.append(ext); + + std::ifstream f(testfile(filename)); + bool result {f.good()}; + f.close(); + + return result; +} + +void clean_file(const std::string& name, const std::string& ext, bool glob = false) { + std::string filename {""}; + filename.append(name); + filename.append("."); + filename.append(ext); + + std::remove(testfile(filename).c_str()); +} +char** to_cstr_array(std::vector in, char** argv) { + int i = 0; + for (auto& str : in) { + argv[i] = new char[str.size()]; + strcpy(argv[i], str.c_str()); + i++; + } + return argv; +} + +void clean_array(size_t argc, char** argv) { + for (size_t i = 0; i < argc; ++i) { + delete[] argv[i]; + } +} + +SCENARIO( "CLI Export Arguments", "[!mayfail]") { + char* args_cli[20]; + std::vector in_args; + in_args.reserve(20); + in_args.emplace_back("gui_test"s); + in_args.emplace_back(testfile("test_cli/20mmbox.stl"s)); + + GIVEN( "Default configuration and a simple 3D model" ) { + WHEN ( "[ ACTION ] is export-gcode with long option") { + in_args.emplace(in_args.cend()-1, "--export-gcode"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("GCode file is created.") { + REQUIRE(file_exists("test_cli/20mmbox"s, "gcode"s)); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "gcode"); + } + WHEN ( "[ ACTION ] is export-gcode with short option") { + in_args.emplace(in_args.cend()-1, "-g"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("GCode file is created.") { + REQUIRE(file_exists("test_cli/20mmbox"s, "gcode"s)); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "gcode"); + } + WHEN ( "[ ACTION ] is export-obj") { + in_args.emplace(in_args.cend()-1, "--export-obj"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("OBJ file is created.") { + REQUIRE(file_exists("test_cli/20mmbox"s, "obj"s)); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "obj"); + } + WHEN ( "[ ACTION ] is export-pov") { + in_args.emplace(in_args.cend()-1, "--export-pov"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("POV file is created.") { + REQUIRE(file_exists("test_cli/20mmbox", "pov")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "pov"); + } + WHEN ( "[ ACTION ] is export-amf") { + in_args.emplace(in_args.cend()-1, "--export-amf"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("AMF file is created.") { + REQUIRE(file_exists("test_cli/20mmbox", "amf")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "amf"); + } + WHEN ( "[ ACTION ] is export-3mf") { + in_args.emplace(in_args.cend()-1, "--export-3mf"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("3MF file is created.") { + REQUIRE(file_exists("test_cli/20mmbox", "3mf")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "3mf"); + } + WHEN ( "[ ACTION ] is export-svg") { + in_args.emplace(in_args.cend()-1, "--export-svg"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("SVG file is created.") { + REQUIRE(file_exists("test_cli/20mmbox", "svg")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "svg"); + } + WHEN ( "[ ACTION ] is export-sla-svg") { + in_args.emplace(in_args.cend()-1, "--export-sla-svg"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("SVG file is created.") { + REQUIRE(file_exists("test_cli/20mmbox", "svg")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "svg"); + } + WHEN ( "[ ACTION ] is sla") { + in_args.emplace(in_args.cend()-1, "--sla"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("SVG file is created.") { + REQUIRE(file_exists("test_cli/20mmbox", "svg")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/20mmbox", "svg", true); + } + WHEN ( "[ ACTION ] is sla and --output-file-format is output_[padded_layer_num].svg") { + in_args.emplace(in_args.cend()-1, "--sla"); + in_args.emplace(in_args.cend()-1, "--output-file-format output_[padded_layer_num].svg"); + in_args.emplace(in_args.cend()-1, "--layer-height 5"); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("SVG files are created.") { + REQUIRE(file_exists("test_cli/output_0", "svg")); + REQUIRE(file_exists("test_cli/output_1", "svg")); + REQUIRE(file_exists("test_cli/output_2", "svg")); + REQUIRE(file_exists("test_cli/output_3", "svg")); + REQUIRE(file_exists("test_cli/output_4", "svg")); + } + clean_array(in_args.size(), args_cli); + clean_file("test_cli/output_0", "svg", true); + clean_file("test_cli/output_1", "svg", true); + clean_file("test_cli/output_2", "svg", true); + clean_file("test_cli/output_3", "svg", true); + clean_file("test_cli/output_4", "svg", true); + } + WHEN ( "[ ACTION ] is save") { + in_args.emplace(in_args.cend()-1, "--save"); + in_args.emplace(in_args.cend()-1, testfile("cfg.ini")); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("Configuration file is created.") { + REQUIRE(file_exists("cfg", "ini")); + } + clean_array(in_args.size(), args_cli); + clean_file("cfg", "ini"); + } + WHEN ( "[ ACTION ] is export-stl and --output option is specified") { + in_args.emplace(in_args.cend()-1, "--export-stl"); + in_args.emplace(in_args.cend()-1, "--output"); + in_args.emplace(in_args.cend()-1, testfile("output.stl")); + CLI().run(in_args.size(), to_cstr_array(in_args, args_cli)); + THEN ("STL file is created.") { + REQUIRE(file_exists("output", "stl")); + } + clean_array(in_args.size(), args_cli); + clean_file("output", "stl"); + } + + } +} + +SCENARIO("CLI Transform arguments", "[!shouldfail]") { + char* args_cli[20]; + std::vector in_args; + in_args.reserve(20); + in_args.emplace_back("gui_test"s); + in_args.emplace_back(testfile("test_cli/20mmbox.stl"s)); + WHEN("Tests are implemented for CLI model transform") { + THEN ("Tests should not fail :D") { + REQUIRE(false); + } + } +} diff --git a/src/test/inputs/test_cli/20mmbox.stl b/src/test/inputs/test_cli/20mmbox.stl new file mode 100644 index 000000000..c7386d4af --- /dev/null +++ b/src/test/inputs/test_cli/20mmbox.stl @@ -0,0 +1,86 @@ +solid Default + facet normal 0.000000e+00 0.000000e+00 -1.000000e+00 + outer loop + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex -1.000000e+01 1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 -1.000000e+00 + outer loop + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex 1.000000e+01 -1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 1.000000e+00 + outer loop + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex -1.000000e+01 -1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 1.000000e+00 + outer loop + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex 1.000000e+01 1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal 1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex 1.000000e+01 -1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex 1.000000e+01 1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal -0.000000e+00 -1.000000e+00 -0.000000e+00 + outer loop + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex 1.000000e+01 -1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal -0.000000e+00 -1.000000e+00 -0.000000e+00 + outer loop + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex 1.000000e+01 -1.000000e+01 1.000000e+01 + vertex -1.000000e+01 -1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal -1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex -1.000000e+01 -1.000000e+01 1.000000e+01 + endloop + endfacet + facet normal -1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex -1.000000e+01 -1.000000e+01 0.000000e+00 + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex -1.000000e+01 1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 1.000000e+00 0.000000e+00 + outer loop + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex -1.000000e+01 1.000000e+01 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 1.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+01 1.000000e+01 0.000000e+00 + vertex -1.000000e+01 1.000000e+01 1.000000e+01 + vertex 1.000000e+01 1.000000e+01 1.000000e+01 + endloop + endfacet +endsolid Default diff --git a/src/test/test_options.hpp.in b/src/test/test_options.hpp.in index 201961cc4..c02f720a3 100644 --- a/src/test/test_options.hpp.in +++ b/src/test/test_options.hpp.in @@ -1,6 +1,14 @@ #ifndef TEST_OPTIONS_HPP +#include /// Directory path, passed in from the outside, for the path to the test inputs dir. constexpr auto* testfile_dir {"@TESTFILE_DIR@"}; +inline std::string testfile(std::string filename) { + std::string result; + result.append(testfile_dir); + result.append(filename); + return result; +} + #endif // TEST_OPTIONS_HPP