M .hgsubstate +1 -1
@@ 1,4 1,4 @@
-c93a13c8d24b789eb83a91e0a8350d58ef888c9f library
+6eb78205ebd8bfc3705fec4630d016a5f9d0b454 library
213d46ad898d376bcb18ce5d3fc02f81f0e98d17 src/lithe
5a97797856da2c6b1a18bf9fe591a64d58b04e19 src/pad
29a29937ee1095e5ddf1655902f1ed09c810bbf3 src/paf
M cmake/Test.cmake +1 -2
@@ 29,7 29,6 @@ set_property(DIRECTORY APPEND PROPERTY C
"${CMAKE_SOURCE_DIR}/library/tests.json" )
set(KRONOS_SUBMIT_TEST_AUTH "" CACHE STRING "Submit test run to database")
-message(STATUS ${KRONOS_SUBMIT_TEST_AUTH})
if (KRONOS_SUBMIT_TEST_AUTH)
@@ 87,7 86,7 @@ foreach(var ${STATIC_TESTS})
target_link_libraries(${test_name} static_test_core)
- set_target_properties(${test_name} PROPERTIES FOLDER apps/tests COMPILE_FLAGS -D_CONSOLE)
+ set_target_properties(${test_name} PROPERTIES FOLDER "static_tests/${test_file}" COMPILE_FLAGS -D_CONSOLE)
add_test( NAME "static.${test_name}"
COMMAND
M configure_tests.py +22 -9
@@ 1,13 1,26 @@
#!/usr/env python
-import json, io, sys
+import json, io, sys, os
-tests = json.loads(open(sys.argv[1]).read())
+basepath = os.path.dirname(sys.argv[1])
-for p in tests["eval"]:
- for t in tests["eval"][p]:
- if "Static" not in tests["eval"][p][t].get("exclude", ""):
- if "expr" in tests["eval"][p][t]:
- sys.stdout.write("%s\n%s\nEval({ %s } nil);" % (p, t, tests["eval"][p][t]["expr"]))
- else:
- sys.stdout.write("%s\n%s\nTest:%s();" % (p, t, t))
No newline at end of file
+def process_tests(package, tests):
+ if "$include" in tests:
+ file = tests["$include"][0]
+ path = tests["$include"][1:]
+ json_file = json.loads(open(basepath + "/" + file).read())
+ for p in path:
+ json_file = json_file.get(p, {})
+ process_tests(package, json_file)
+ else:
+ for t in tests:
+ if "Static" not in tests[t].get("exclude", { }):
+ if "expr" in tests[t]:
+ sys.stdout.write("%s\n%s\nEval({ %s } nil);" % (package, t, tests[t]["expr"]))
+ else:
+ sys.stdout.write("%s\n%s\nTest:%s();" % (package, t, t))
+
+master = json.loads(open(sys.argv[1]).read()).get("eval", {})
+
+for package in master:
+ process_tests(package, master[package])
No newline at end of file
M src/driver/ktests.cpp +45 -5
@@ 195,6 195,46 @@ static picojson::object BatchRunner(cons
picojson::object GetRunInfo();
picojson::array SplitSemVer(const std::string& a);
+Packages::DefaultClient bbClient;
+
+void ProcessIncludes(picojson::value& val) {
+ if (val.is<picojson::object>()) {
+ picojson::object& obj{ val.get<picojson::object>() };
+ auto inc = obj.find("$include");
+ if (inc != obj.end() && inc->second.is<picojson::array>()) {
+ auto incpath = inc->second.get<picojson::array>();
+ if (incpath.empty()) throw std::runtime_error("Bad include path");
+ auto filepath = incpath[0].to_str();
+
+ std::string includeJsonPath = bbClient.Resolve(CLOpts::package(), filepath, CLOpts::package_version());
+ std::ifstream includeJsonStream{ includeJsonPath };
+ if (includeJsonStream.is_open() == false)
+ throw std::runtime_error("Could not open included '" + includeJsonPath + "'");
+
+ picojson::value includeJson;
+ std::string err;
+ err = picojson::parse(includeJson, includeJsonStream);
+ if (err.size()) {
+ throw std::runtime_error("Parse error while reading '" + includeJsonPath + "': " + err);
+ }
+
+ for (int i = 1; i < incpath.size(); ++i) {
+ auto pseg = incpath[i].to_str();
+ if (includeJson.contains(pseg)) {
+ // convert from reference to make a copy
+ includeJson = (picojson::value)includeJson.get(pseg);
+ } else {
+ throw std::runtime_error("Included file does not contain '" + incpath[i].to_str() + "'");
+ }
+ }
+ val = includeJson;
+ } else {
+ for (auto&& kv : obj) {
+ ProcessIncludes(kv.second);
+ }
+ }
+ }
+}
int main(int argc, const char* arg[]) {
@@ 260,8 300,6 @@ int main(int argc, const char* arg[]) {
std::clog << "\n";
- Packages::DefaultClient bbClient;
-
auto testDataFilePath = bbClient.Resolve(CLOpts::package(), "tests.json", CLOpts::package_version());
std::ifstream testDataStream(testDataFilePath);
if (!testDataStream.is_open()) throw std::runtime_error("Could not load tests for module [" + CLOpts::package() + " " + CLOpts::package_version() + "]");
@@ 269,6 307,8 @@ int main(int argc, const char* arg[]) {
picojson::value testData;
auto jsonError = picojson::parse(testData, testDataStream);
testDataStream.close();
+
+ ProcessIncludes(testData);
if (jsonError.size()) {
throw std::runtime_error(jsonError);
@@ 572,12 612,12 @@ std::string Diff(const char *scheme, con
TestSummary.Unknown(testId);
testData["status"] = "new";
- return testData["result"].contains("text") ? testData["result"].get("text").to_str() : "";
+ return ( testData["result"].contains("text") ? testData["result"].get("text").to_str() + "\n" : "" );
} catch (std::exception & e) {
testData["status"] = "fail";
testData["diff_exception"] = e.what();
TestSummary.Fail(testId);
- return "";
+ return e.what();
}
}
@@ 599,7 639,7 @@ picojson::object BatchRunner(const char*
auto& testCases = inPackage.second.get<picojson::object>();
for (auto &test : testCases) {
if (std::regex_search(scheme + ":"s + inPackage.first + "/" + test.first, filter)) {
- std::clog << " - " << test.first << " ... ";
+ std::clog << " - " << (test.second.contains("label") ? test.second.get("label").to_str() : test.first) << " ... ";
auto startTime = std::chrono::steady_clock::now();
picojson::object testResult;
auto testCase = "Test:" + test.first;
M src/driver/package.cpp +14 -0
@@ 456,6 456,20 @@ namespace Packages {
return false;
}
+ std::string uint128::digest() const {
+ uint32_t scramble = 0;
+ for (auto& w : dw) {
+ scramble ^= w;
+ }
+ const char b64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
+ std::string d = "";
+ for (int i = 0; i < 5; i++) {
+ d.push_back(b64[scramble & 0x3f]);
+ scramble >>= 6;
+ }
+ return d;
+ }
+
static size_t writeHex(char * const buf, const uint128& u128) {
const char hexdigit[] = {
'0','1','2','3','4','5','6','7','8','9','0','a','b','c','d','e','f'
M src/driver/package.h +1 -0
@@ 79,6 79,7 @@ namespace Packages {
bool is_zero() const { for (auto d : dw) if (d) return false; return true; }
operator std::string() const;
std::string to_str() const { return *this; }
+ std::string digest() const;
};
uint128 operator*(uint128 a, uint128 b);
M src/website/makehtml.cpp +27 -16
@@ 760,25 760,36 @@ int link_page(const char **files, int nu
tmp << link;
if (link.contains("tests")) {
- auto& t = link.get("tests");
+ auto writeFile = [](const std::string & name, const std::string& content) {
+ std::ofstream write{ name };
+ if (!write.is_open()) throw std::runtime_error("Could not write '" + name + "'");
+ write << content;
+ };
+
Packages::MakeMultiLevelPath("tests/");
- for (auto& s : t.get("source").get<picojson::object>()) {
- auto testFile = "tests/" + s.first + ".k";
- std::clog << " - Tests: " << s.first << "\n";
- std::ofstream tsource(testFile);
- if (tsource.is_open() == false) throw std::runtime_error("Could not write test " + testFile);
- tsource << s.second.to_str();
- }
+ for (auto&& t : link.get("tests").get<picojson::object>()) {
+ auto&& name = t.first;
+ auto&& data = t.second.get<picojson::object>();
+ std::string source;
+ auto& src = data.at("source");
+
+ if (src.is<picojson::array>()) {
+ for (auto& s : src.get<picojson::array>()) {
+ source += s.to_str();
+ source.push_back('\n');
+ }
+ } else {
+ source = src.get<std::string>();
+ }
- std::ofstream tests{ "tests.json" };
- if (tests.is_open() == false) throw std::runtime_error("Could not write testcase file");
-
- tests << picojson::value{
- picojson::object{
- { "audio", t.get("audio") },
- { "eval", t.get("eval") }
- } };
+ writeFile("tests/" + name + ".k", source);
+ writeFile("tests/" + name + ".json", picojson::value{
+ picojson::object {
+ { "audio", data.at("audio") },
+ { "eval", data.at("eval") } }
+ }.serialize());
+ }
}
std::clog << "\n";
M src/website/md2json.cpp +27 -12
@@ 2,6 2,7 @@
#include <fstream>
#include <sstream>
#include <cstdlib>
+#include <regex>
#include <sys/stat.h>
#include <time.h>
@@ 410,7 411,7 @@ picojson::value render_kronos_code(std::
auto tmpfile = "repl_"s + Packages::fnv1a(id.data(), id.size()).to_str() + ".mp3";
repl.evaluate(
- ":no:" + code + " Actions:Render(\"" + escape(tmpfile) + "\" 441000 44100 { :no:snd } )");
+ "Actions:Render(\"" + escape(tmpfile) + "\" 441000 44100 { " + code + " snd } )");
repl.audio.emplace_back(code);
@@ 526,17 527,30 @@ int compile_page(const char *in, const c
picojson::object audioTests;
picojson::object evalTests;
- int c = 1;
+ auto uniqueName = [names = std::unordered_set<std::string>{}](std::string content) mutable {
+ auto hash = Packages::fnv1a(content.data(), content.size()).digest();
+ std::string nm = hash;
+ int suffix = 1;
+ while (names.count(nm)) {
+ nm = hash + "-" + std::to_string(suffix++);
+ }
+ names.emplace(nm);
+ return nm;
+ };
+
+ std::regex whitespace("\\S+");
for (auto& a : repl.audio) {
- auto caseName = "Audio-" + std::to_string(c++);
+ auto caseName = "Audio-" + uniqueName(a);
repl.code += "\n:Test:" + caseName + "() { " + a + " snd }\n";
- audioTests[caseName] = picojson::object{};
+ a = std::regex_replace(a, whitespace, " ", std::regex_constants::match_any);
+ audioTests[caseName] = picojson::object{ {"label", a} };
}
for (auto& e : repl.eval) {
- auto caseName = "Eval-" + std::to_string(c++);
+ auto caseName = "Eval-" + uniqueName(e);
repl.code += "\n:Test:" + caseName + "() { Handle(" + e + " '_ ) }\n";
- evalTests[caseName] = picojson::object{};
+ e = std::regex_replace(e, whitespace, " ", std::regex_constants::match_any).substr(6);
+ evalTests[caseName] = picojson::object{ {"label", e} };
}
picojson::object json{
@@ 559,7 573,7 @@ int compile_page(const char *in, const c
auto lastSep = basename.find_last_of("\\/");
if (lastSep == basename.npos) lastSep = 0;
- std::string name = "Web-";
+ std::string name = "";
for(;lastSep<basename.size();++lastSep) {
auto c = basename[lastSep];
if (isalnum(c)) {
@@ 569,11 583,12 @@ int compile_page(const char *in, const c
}
}
- json["tests"] = picojson::object {
- { "source", picojson::object{{name, repl.code } } },
- { "audio", picojson::object{{name, audioTests } } },
- { "eval", picojson::object{{name, evalTests } } }
- };
+ json["tests"] = picojson::object{ { name,
+ picojson::object {
+ { "source", repl.code },
+ { "audio", audioTests },
+ { "eval", evalTests },
+ } } };
}
write << json;
M version.txt +1 -1
@@ 1,1 1,1 @@
-0.14.1
No newline at end of file
+0.14.2
No newline at end of file