diff --git a/.gitignore b/.gitignore index 145d65b..e7e358f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /.vscode /maintenance/test/data .clang-format -/frontend/node_modules \ No newline at end of file +/frontend/node_modules +/build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e69de29..bb175b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.15.2) +project(BookStore) +file(GLOB_RECURSE main_src backend/src/*.cpp) +include_directories(${PROJECT_SOURCE_DIR}/backend/include) +add_executable(code ${main_src}) +add_subdirectory(maintenance/test) +include(maintenance/test/ctest_config) \ No newline at end of file diff --git a/backend/include/builtin-cli.h b/backend/include/builtin-cli.h new file mode 100644 index 0000000..e69de29 diff --git a/backend/include/database.h b/backend/include/database.h new file mode 100644 index 0000000..e69de29 diff --git a/backend/include/drivearray.h b/backend/include/drivearray.h new file mode 100644 index 0000000..e69de29 diff --git a/backend/include/engine.h b/backend/include/engine.h new file mode 100644 index 0000000..e69de29 diff --git a/backend/include/schedule.h b/backend/include/schedule.h new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/builtin-cli.cpp b/backend/src/builtin-cli.cpp new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/database.cpp b/backend/src/database.cpp new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/drivearray.cpp b/backend/src/drivearray.cpp new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/engine.cpp b/backend/src/engine.cpp new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/main.cpp b/backend/src/main.cpp new file mode 100644 index 0000000..c3aeba4 --- /dev/null +++ b/backend/src/main.cpp @@ -0,0 +1 @@ +int main() { return 0; } \ No newline at end of file diff --git a/backend/src/schedule.cpp b/backend/src/schedule.cpp new file mode 100644 index 0000000..e69de29 diff --git a/docs/develop/总体设计文档.md b/docs/develop/总体设计文档.md index 9aec09e..537cca4 100644 --- a/docs/develop/总体设计文档.md +++ b/docs/develop/总体设计文档.md @@ -43,7 +43,7 @@ memoryriver类维护一个缓存,简单地缓存高频访问和连续访问; ### 引擎模块 具体执行业务,一次请求对应且只对应一次引擎模块API调用,支持并行(API阻塞但可同时运行)。当API入口函数被调用后,执行相关具体操作(包括会话管理),然后返回响应。 -### 通信与调度模块 +### 调度模块 负责对外提供文本模式的交互,以及维护请求级的并行。从流`request`读取文本格式的命令,并把内部数据格式的响应翻译成文本格式,向引擎模块发送请求(调用引擎模块对外提供的API),并把响应返回到流`response`。支持条件允许时的并行: - 输入子模块从`request`读取请求,打上时间戳,分独占式请求和可并行请求按时间戳顺序分批处理。处理一个请求时,启动一个工作线程调用翻译子模块。同一个会话的请求不可并行,把`std::thread`move进相应的该会话的工作线程句柄队列,下一个join前一个。成批执行可并行请求时,启动完当前批次所有工作线程后join每个session的最后一个工作线程,结束之后就可以执行下一批了。为了在服务模式下处理时有时无的请求,两个流在读取时是阻塞的(自己封装一个,用`std::condition_variable`通讯),并且读一个处理一个,碰到“不属于自己批次”的东西才算一批结束。 - 翻译子模块(入口函数本身阻塞)join完同一个session的上一个请求后,向引擎模块API发送请求,得到响应后把响应数据传给输出子模块的调用接口(阻塞但可并行,处理部分同时运行,上锁后直接输出到`response`,输出本身不同时进行)。 diff --git a/maintenance/test/CMakeLists.txt b/maintenance/test/CMakeLists.txt new file mode 100644 index 0000000..eadfea1 --- /dev/null +++ b/maintenance/test/CMakeLists.txt @@ -0,0 +1,7 @@ +set(PROJECT_NAME ${CMAKE_PROJECT_NAME}) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_FLAGS "-O2") +set(ENV{MAKEFLAGS} "-j16") +include_directories(${PROJECT_SOURCE_DIR}/external) +add_executable(test-core test-core.cpp) +target_precompile_headers(test-core PUBLIC ${PROJECT_SOURCE_DIR}/external/clipp/clipp.h ${PROJECT_SOURCE_DIR}/external/json/json.hpp) \ No newline at end of file diff --git a/maintenance/test/config.json b/maintenance/test/config.json new file mode 100644 index 0000000..702b1cd --- /dev/null +++ b/maintenance/test/config.json @@ -0,0 +1,8 @@ +{ + "StatusInterpreter":{"0":"OK","1":"Wrong Answer","139":"Segmentation fault","137":"(Killed) maybe Time Limit Error"}, + "Cases": + [ + {"command":"cd /workspaces/BH-Bookstore-2023 && timeout -s 9 1s build/code /tmp/B0.out && diff -u /tmp/B0.out test/testcases/basic-testcases/test0.out >/tmp/diff_B0","uid":"#0","tid":"/1/0"}, + {"command":"cd /workspaces/BH-Bookstore-2023 && timeout -s 9 3s build/code /tmp/I0.out && diff -u /tmp/I0.out test/testcases/bigint-testcases/BigIntegerTest0.out >/tmp/diff_I0","uid":"#14","tid":"/2/0"}, + ] +} \ No newline at end of file diff --git a/maintenance/test/ctest_config b/maintenance/test/ctest_config new file mode 100644 index 0000000..e69de29 diff --git a/maintenance/test/test-core.cpp b/maintenance/test/test-core.cpp new file mode 100644 index 0000000..a4f0b1e --- /dev/null +++ b/maintenance/test/test-core.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include + +#include "clipp/clipp.h" +#include "json/json.hpp" +std::string getDirectory(const std::string &filepath) { + size_t found = filepath.find_last_of("/\\"); + return filepath.substr(0, found); +} +#define DeEscape std::string +int main(int argc, char *argv[]) { + bool continuous = false; + bool test_all = false; + bool test_listed_cases = false; + std::vector listed_cases; + std::string filePath(__FILE__); + std::string directoryPath = getDirectory(filePath); + std::string config_file = directoryPath + "/config.json"; + bool use_custoem_config = false; + std::string new_config = ""; + bool test_sub_tree = false; + std::string sub_tree; + bool read_lists_from_stdin = false; + bool read_lists_from_file = false; + std::string list_file_path; + // clang-format off + auto cli=( + clipp::option("--config").set(config_file).doc("config file path") & clipp::value("config file path",config_file), + clipp::option("-c","--continuous").set(continuous).doc("continuous mode"), + ( + clipp::required("-a","--all").set(test_all).doc("test all")| + (clipp::required("-s","--subtree").set(test_sub_tree).doc("test subtree") & clipp::value("subtree",sub_tree))| + (clipp::required("-l","--list").set(test_listed_cases).doc("test listed cases") & clipp::values("cases",listed_cases))| + (clipp::required("-i","--stdin").set(read_lists_from_stdin).doc("read test list from stdin"))| + (clipp::required("-f","--filelist").set(read_lists_from_file).doc("read test list from file") & clipp::value("file",list_file_path)) + ) + ); + // clang-format on + if (!clipp::parse(argc, argv, cli)) { + std::cout << clipp::make_man_page(cli, argv[0]); + return 0; + } + if (use_custoem_config) config_file = new_config; + using json = nlohmann::json; + std::ifstream conf(config_file); + json config = json::parse(conf); + std::unordered_map index; + for (int i = 0; i < config["Cases"].size(); i++) { + index[DeEscape(config["Cases"][i]["tid"])] = + DeEscape(config["Cases"][i]["command"]); + index[DeEscape(config["Cases"][i]["uid"])] = + DeEscape(config["Cases"][i]["command"]); + } + if (test_all) { + listed_cases.clear(); + for (int i = 0; i < config["Cases"].size(); i++) { + listed_cases.push_back(DeEscape(config["Cases"][i]["tid"])); + } + } else if (test_sub_tree) { + listed_cases.clear(); + for (int i = 0; i < config["Cases"].size(); i++) { + if (DeEscape(config["Cases"][i]["tid"]).find(sub_tree) == 0 && + (DeEscape(config["Cases"][i]["tid"]).length() == sub_tree.length() || + DeEscape(config["Cases"][i]["tid"])[sub_tree.length()] == '/')) { + listed_cases.push_back(DeEscape(config["Cases"][i]["tid"])); + } + } + } else if (read_lists_from_stdin) { + listed_cases.clear(); + std::string line; + while (std::getline(std::cin, line)) { + listed_cases.push_back(line); + } + } else if (read_lists_from_file) { + listed_cases.clear(); + std::ifstream list_file(list_file_path); + std::string line; + while (std::getline(list_file, line)) { + listed_cases.push_back(line); + } + } else if (!test_listed_cases) + throw std::runtime_error("No test cases specified"); + int total_cases = 0, total_passed = 0; + for (int i = 0; i < listed_cases.size(); i++) { + std::cerr << "Testing " << listed_cases[i] << std::endl; + if (index.find(listed_cases[i]) == index.end()) { + std::cerr << "Test " << listed_cases[i] << " not found" << std::endl; + std::cerr << '\n' << std::endl; + continue; + } + std::cerr << "Command = " << index[listed_cases[i]] << std::endl; + int status = system(index[listed_cases[i]].c_str()) / 256; + total_cases++; + if (status == 0) total_passed++; + if (status == 0) { + std::cerr << "Test " << listed_cases[i] << " passed" << std::endl; + } else { + std::cerr << "Test " << listed_cases[i] << " failed" << std::endl; + std::cerr << "status code = " << status << std::endl; + std::cerr + << "Brief info: " + << (config["StatusInterpreter"].contains(std::to_string(status)) + ? DeEscape( + config["StatusInterpreter"][std::to_string(status)]) + : std::string("Unknown Error")) + << std::endl; + if (!continuous) { + std::cerr << total_passed << "/" << total_cases << " cases passed" + << std::endl; + return status; + } + } + std::cerr << total_passed << "/" << total_cases << " cases passed" + << std::endl; + std::cerr << '\n' << std::endl; + } + return 0; +} \ No newline at end of file