@@ -60,7 +60,7 @@ document.getElementById('toggle-sequence').addEventListener('click', function (e
|
||||
std::string createResultItem(const TestRunOutput& result, const std::string& status, bool shouldHide) {
|
||||
std::ostringstream html;
|
||||
html << "<div class=\"test " << status << (shouldHide ? " hide" : "") << "\">\n";
|
||||
html << R"(<h2><span class="label">)" << status << "</span> " << result.id << "</h2>\n";
|
||||
html << R"(<h2><span class="label">)" << status << "</span> " << result.id << "</h2>\n";
|
||||
|
||||
html << "<p><pre>"s << result.expression << "</pre></p>\n"s;
|
||||
if (result.passed) {
|
||||
@@ -98,11 +98,11 @@ std::string createResultPage(const TestStats& stats, bool shuffle, uint32_t seed
|
||||
|
||||
// Failed tests
|
||||
if (unsuccessful) {
|
||||
resultsPage << "<p><strong>Failed tests:</strong>";
|
||||
resultsPage << "<p><strong>Failed tests:</strong>";
|
||||
for (const auto& failed : stats.failed) {
|
||||
resultsPage << failed.id << " ";
|
||||
}
|
||||
resultsPage << "<p><strong>Errored tests:</strong>";
|
||||
resultsPage << "<p><strong>Errored tests:</strong>";
|
||||
for (const auto& errored : stats.errored) {
|
||||
resultsPage << errored.id << " ";
|
||||
}
|
||||
@@ -127,9 +127,9 @@ std::string createResultPage(const TestStats& stats, bool shuffle, uint32_t seed
|
||||
// Script
|
||||
resultsPage << resultsScript;
|
||||
|
||||
// Tests
|
||||
// Tests
|
||||
resultsPage << "<div class=\"tests\">\n";
|
||||
const auto appendResult = [&] (const auto& results, const std::string& status, bool hide = false) {
|
||||
const auto appendResult = [&](const auto& results, const std::string& status, bool hide = false) {
|
||||
for (const auto& result : results) {
|
||||
resultsPage << createResultItem(result, status, hide);
|
||||
}
|
||||
@@ -153,16 +153,21 @@ void printStats(const TestStats& stats) {
|
||||
printf(ANSI_COLOR_GREEN "%zu passed (%.1lf%%)" ANSI_COLOR_RESET "\n", passedTests, 100.0 * passedTests / count);
|
||||
}
|
||||
if (std::size_t ignorePassedTests = stats.ignorePassed.size()) {
|
||||
printf(ANSI_COLOR_YELLOW "%zu passed but were ignored (%.1lf%%)" ANSI_COLOR_RESET "\n", ignorePassedTests, 100.0 * ignorePassedTests / count);
|
||||
printf(ANSI_COLOR_YELLOW "%zu passed but were ignored (%.1lf%%)" ANSI_COLOR_RESET "\n",
|
||||
ignorePassedTests,
|
||||
100.0 * ignorePassedTests / count);
|
||||
}
|
||||
if (std::size_t ignoreFailedTests = stats.ignoreFailed.size()) {
|
||||
printf(ANSI_COLOR_LIGHT_GRAY "%zu ignored (%.1lf%%)" ANSI_COLOR_RESET "\n", ignoreFailedTests, 100.0 * ignoreFailedTests / count);
|
||||
printf(ANSI_COLOR_LIGHT_GRAY "%zu ignored (%.1lf%%)" ANSI_COLOR_RESET "\n",
|
||||
ignoreFailedTests,
|
||||
100.0 * ignoreFailedTests / count);
|
||||
}
|
||||
if (std::size_t failedTests = stats.failed.size()) {
|
||||
printf(ANSI_COLOR_RED "%zu failed (%.1lf%%)" ANSI_COLOR_RESET "\n", failedTests, 100.0 * failedTests / count);
|
||||
}
|
||||
if (std::size_t erroredTests = stats.errored.size()) {
|
||||
printf(ANSI_COLOR_RED "%zu errored (%.1lf%%)" ANSI_COLOR_RESET "\n", erroredTests, 100.0 * erroredTests / count);
|
||||
printf(
|
||||
ANSI_COLOR_RED "%zu errored (%.1lf%%)" ANSI_COLOR_RESET "\n", erroredTests, 100.0 * erroredTests / count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#define ANSI_COLOR_RED "\x1b[31m"
|
||||
#define ANSI_COLOR_GREEN "\x1b[32m"
|
||||
#define ANSI_COLOR_YELLOW "\x1b[33m"
|
||||
#define ANSI_COLOR_RED "\x1b[31m"
|
||||
#define ANSI_COLOR_GREEN "\x1b[32m"
|
||||
#define ANSI_COLOR_YELLOW "\x1b[33m"
|
||||
#define ANSI_COLOR_LIGHT_GRAY "\x1b[90m"
|
||||
#define ANSI_COLOR_RESET "\x1b[0m"
|
||||
#define ANSI_COLOR_RESET "\x1b[0m"
|
||||
|
||||
class TestStats;
|
||||
|
||||
|
||||
@@ -28,29 +28,27 @@ using namespace std::literals;
|
||||
namespace {
|
||||
|
||||
void writeJSON(rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer, const Value& value) {
|
||||
value.match(
|
||||
[&] (const NullValue&) { writer.Null(); },
|
||||
[&] (bool b) { writer.Bool(b); },
|
||||
[&] (uint64_t u) { writer.Uint64(u); },
|
||||
[&] (int64_t i) { writer.Int64(i); },
|
||||
[&] (double d) { d == std::floor(d) ? writer.Int64(static_cast<int64_t>(d)) : writer.Double(d); },
|
||||
[&] (const std::string& s) { writer.String(s); },
|
||||
[&] (const std::vector<Value>& arr) {
|
||||
writer.StartArray();
|
||||
for(const auto& item : arr) {
|
||||
writeJSON(writer, item);
|
||||
}
|
||||
writer.EndArray();
|
||||
},
|
||||
[&] (const std::unordered_map<std::string, Value>& obj) {
|
||||
writer.StartObject();
|
||||
for(const auto& entry : obj) {
|
||||
writer.Key(entry.first.c_str());
|
||||
writeJSON(writer, entry.second);
|
||||
}
|
||||
writer.EndObject();
|
||||
}
|
||||
);
|
||||
value.match([&](const NullValue&) { writer.Null(); },
|
||||
[&](bool b) { writer.Bool(b); },
|
||||
[&](uint64_t u) { writer.Uint64(u); },
|
||||
[&](int64_t i) { writer.Int64(i); },
|
||||
[&](double d) { d == std::floor(d) ? writer.Int64(static_cast<int64_t>(d)) : writer.Double(d); },
|
||||
[&](const std::string& s) { writer.String(s); },
|
||||
[&](const std::vector<Value>& arr) {
|
||||
writer.StartArray();
|
||||
for (const auto& item : arr) {
|
||||
writeJSON(writer, item);
|
||||
}
|
||||
writer.EndArray();
|
||||
},
|
||||
[&](const std::unordered_map<std::string, Value>& obj) {
|
||||
writer.StartObject();
|
||||
for (const auto& entry : obj) {
|
||||
writer.Key(entry.first.c_str());
|
||||
writeJSON(writer, entry.second);
|
||||
}
|
||||
writer.EndObject();
|
||||
});
|
||||
}
|
||||
|
||||
using ErrorMessage = std::string;
|
||||
@@ -58,21 +56,21 @@ using JSONReply = variant<JSDocument, ErrorMessage>;
|
||||
JSONReply readJson(const filesystem::path& jsonPath) {
|
||||
auto maybeJSON = util::readFile(jsonPath);
|
||||
if (!maybeJSON) {
|
||||
return { "Unable to open file "s + jsonPath.string() };
|
||||
return {"Unable to open file "s + jsonPath.string()};
|
||||
}
|
||||
|
||||
JSDocument document;
|
||||
document.Parse<rapidjson::kParseFullPrecisionFlag>(*maybeJSON);
|
||||
if (document.HasParseError()) {
|
||||
return { formatJSONParseError(document) };
|
||||
return {formatJSONParseError(document)};
|
||||
}
|
||||
|
||||
return { std::move(document) };
|
||||
return {std::move(document)};
|
||||
}
|
||||
|
||||
std::string toString(const JSValue& value) {
|
||||
assert(value.IsString());
|
||||
return { value.GetString(), value.GetStringLength() };
|
||||
return {value.GetString(), value.GetStringLength()};
|
||||
}
|
||||
|
||||
std::optional<Value> toValue(const JSValue& jsvalue) {
|
||||
@@ -314,10 +312,9 @@ bool parseInputs(const JSValue& inputsValue, TestData& data) {
|
||||
std::tuple<filesystem::path, std::vector<filesystem::path>, bool, uint32_t> parseArguments(int argc, char** argv) {
|
||||
args::ArgumentParser argumentParser("Mapbox GL Expression Test Runner");
|
||||
|
||||
args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", { 'h', "help" });
|
||||
args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"});
|
||||
args::Flag shuffleFlag(argumentParser, "shuffle", "Toggle shuffling the tests order", {'s', "shuffle"});
|
||||
args::ValueFlag<uint32_t> seedValue(argumentParser, "seed", "Shuffle seed (default: random)",
|
||||
{ "seed" });
|
||||
args::ValueFlag<uint32_t> seedValue(argumentParser, "seed", "Shuffle seed (default: random)", {"seed"});
|
||||
args::PositionalList<std::string> testNameValues(argumentParser, "URL", "Test name(s)");
|
||||
args::ValueFlag<std::string> testFilterValue(argumentParser, "filter", "Test filter regex", {'f', "filter"});
|
||||
|
||||
@@ -342,7 +339,7 @@ std::tuple<filesystem::path, std::vector<filesystem::path>, bool, uint32_t> pars
|
||||
exit(2);
|
||||
}
|
||||
|
||||
filesystem::path rootPath {std::string(TEST_RUNNER_ROOT_PATH).append("/metrics/integration/expression-tests")};
|
||||
filesystem::path rootPath{std::string(TEST_RUNNER_ROOT_PATH).append("/metrics/integration/expression-tests")};
|
||||
if (!filesystem::exists(rootPath)) {
|
||||
Log::Error(Event::General, "Test path '" + rootPath.string() + "' does not exist.");
|
||||
exit(3);
|
||||
@@ -377,10 +374,10 @@ std::tuple<filesystem::path, std::vector<filesystem::path>, bool, uint32_t> pars
|
||||
}
|
||||
}
|
||||
|
||||
return Arguments{ std::move(rootPath),
|
||||
std::move(testPaths),
|
||||
shuffleFlag ? args::get(shuffleFlag) : false,
|
||||
seedValue ? args::get(seedValue) : 1u };
|
||||
return Arguments{std::move(rootPath),
|
||||
std::move(testPaths),
|
||||
shuffleFlag ? args::get(shuffleFlag) : false,
|
||||
seedValue ? args::get(seedValue) : 1u};
|
||||
}
|
||||
|
||||
Ignores parseExpressionIgnores() {
|
||||
@@ -392,12 +389,12 @@ Ignores parseExpressionIgnores() {
|
||||
}
|
||||
|
||||
for (const auto& property : maybeIgnores.get<JSDocument>().GetObject()) {
|
||||
std::string id{ toString(property.name) };
|
||||
std::string id{toString(property.name)};
|
||||
// Keep only expression-test ignores
|
||||
if (id.rfind("expression-tests", 0) != 0) {
|
||||
continue;
|
||||
}
|
||||
std::string reason{ toString(property.value) };
|
||||
std::string reason{toString(property.value)};
|
||||
ignores.emplace_back(std::move(id), std::move(reason));
|
||||
}
|
||||
|
||||
@@ -474,7 +471,8 @@ Value toValue(const Compiled& compiled) {
|
||||
std::optional<Value> toValue(const expression::Value& exprValue) {
|
||||
return exprValue.match(
|
||||
[](const Color& c) -> std::optional<Value> {
|
||||
std::vector<Value> color { static_cast<double>(c.r), static_cast<double>(c.g), static_cast<double>(c.b), static_cast<double>(c.a) };
|
||||
std::vector<Value> color{
|
||||
static_cast<double>(c.r), static_cast<double>(c.g), static_cast<double>(c.b), static_cast<double>(c.a)};
|
||||
return {Value{std::move(color)}};
|
||||
},
|
||||
[](const expression::Formatted& formatted) -> std::optional<Value> { return {formatted.toObject()}; },
|
||||
@@ -503,11 +501,10 @@ std::unique_ptr<style::expression::Expression> parseExpression(const JSValue& va
|
||||
std::optional<PropertySpec>& spec,
|
||||
TestResult& result) {
|
||||
std::optional<style::expression::type::Type> expected = spec ? toExpressionType(*spec) : std::nullopt;
|
||||
expression::ParsingContext ctx = expected ? expression::ParsingContext(*expected) :
|
||||
expression::ParsingContext();
|
||||
expression::ParsingContext ctx = expected ? expression::ParsingContext(*expected) : expression::ParsingContext();
|
||||
Convertible convertible(&value);
|
||||
expression::ParseResult parsed;
|
||||
if (value.IsObject() && !value.IsArray() && expected){
|
||||
if (value.IsObject() && !value.IsArray() && expected) {
|
||||
Error error;
|
||||
parsed = convertFunctionToExpression(*expected, convertible, error, false /*convert tokens*/);
|
||||
if (!parsed) {
|
||||
|
||||
@@ -32,10 +32,8 @@ struct Input {
|
||||
|
||||
struct Compiled {
|
||||
bool operator==(const Compiled& other) const {
|
||||
bool typeEqual = success == other.success &&
|
||||
isFeatureConstant == other.isFeatureConstant &&
|
||||
isZoomConstant == other.isZoomConstant &&
|
||||
serializedType == other.serializedType &&
|
||||
bool typeEqual = success == other.success && isFeatureConstant == other.isFeatureConstant &&
|
||||
isZoomConstant == other.isZoomConstant && serializedType == other.serializedType &&
|
||||
errors == other.errors;
|
||||
return typeEqual;
|
||||
}
|
||||
@@ -99,4 +97,3 @@ std::unique_ptr<style::expression::Expression> parseExpression(const JSValue&,
|
||||
std::unique_ptr<style::expression::Expression> parseExpression(const std::optional<Value>&,
|
||||
std::optional<PropertySpec>&,
|
||||
TestResult&);
|
||||
|
||||
|
||||
@@ -18,15 +18,16 @@ using namespace std::literals;
|
||||
namespace {
|
||||
|
||||
std::string simpleDiff(const Value& result, const Value& expected) {
|
||||
std::vector<std::string> resultTokens {tokenize(toJSON(result, 2, true))};
|
||||
std::vector<std::string> expectedTokens {tokenize(toJSON(expected, 2, true))};
|
||||
std::vector<std::string> resultTokens{tokenize(toJSON(result, 2, true))};
|
||||
std::vector<std::string> expectedTokens{tokenize(toJSON(expected, 2, true))};
|
||||
std::size_t maxLength = std::max(resultTokens.size(), expectedTokens.size());
|
||||
std::ostringstream diff;
|
||||
const auto flush = [] (const std::vector<std::string>& vec, std::size_t pos, std::ostringstream& out, std::string separator) {
|
||||
for (std::size_t j = pos; j < vec.size(); ++j) {
|
||||
out << separator << vec[j] << std::endl;
|
||||
}
|
||||
};
|
||||
const auto flush =
|
||||
[](const std::vector<std::string>& vec, std::size_t pos, std::ostringstream& out, std::string separator) {
|
||||
for (std::size_t j = pos; j < vec.size(); ++j) {
|
||||
out << separator << vec[j] << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < maxLength; ++i) {
|
||||
if (resultTokens.size() <= i) {
|
||||
@@ -109,8 +110,8 @@ TestRunOutput runExpressionTest(TestData& data, const std::string& rootPath, con
|
||||
evaluationResult = expression->evaluate(
|
||||
input.zoom, input.feature, input.heatmapDensity, input.availableImages, *input.canonical);
|
||||
} else {
|
||||
evaluationResult =
|
||||
expression->evaluate(input.zoom, input.feature, input.heatmapDensity, input.availableImages);
|
||||
evaluationResult = expression->evaluate(
|
||||
input.zoom, input.feature, input.heatmapDensity, input.availableImages);
|
||||
}
|
||||
if (!evaluationResult) {
|
||||
std::unordered_map<std::string, Value> error{{"error", Value{evaluationResult.error().message}}};
|
||||
@@ -138,8 +139,7 @@ TestRunOutput runExpressionTest(TestData& data, const std::string& rootPath, con
|
||||
auto recompiledExpression = parseExpression(data.result.serialized, data.spec, data.recompiled);
|
||||
if (recompiledExpression) {
|
||||
evaluateExpression(recompiledExpression, data.recompiled);
|
||||
rewriteRoundtrippedType(data.expected.compiled.serializedType,
|
||||
data.recompiled.compiled.serializedType);
|
||||
rewriteRoundtrippedType(data.expected.compiled.serializedType, data.recompiled.compiled.serializedType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ TestRunOutput runExpressionTest(TestData& data, const std::string& rootPath, con
|
||||
|
||||
if (data.expected.compiled.success) {
|
||||
serializationOk = compileOk && deepEqual(data.result.serialized, data.expected.serialized);
|
||||
recompileOk = compileOk && data.recompiled.compiled == data.expected.compiled;
|
||||
recompileOk = compileOk && data.recompiled.compiled == data.expected.compiled;
|
||||
roundTripOk = recompileOk && deepEqual(data.recompiled.outputs, data.expected.outputs);
|
||||
}
|
||||
|
||||
@@ -168,36 +168,28 @@ TestRunOutput runExpressionTest(TestData& data, const std::string& rootPath, con
|
||||
if (!compileOk) {
|
||||
auto resultValue = toValue(data.result.compiled);
|
||||
auto expectedValue = toValue(data.expected.compiled);
|
||||
output.text += "Compiled expression difference:\n"s +
|
||||
simpleDiff(resultValue, expectedValue) +
|
||||
"\n"s;
|
||||
output.text += "Compiled expression difference:\n"s + simpleDiff(resultValue, expectedValue) + "\n"s;
|
||||
}
|
||||
|
||||
if (compileOk && !serializationOk) {
|
||||
auto diff = simpleDiff(data.expected.serialized.value_or(Value{}),
|
||||
data.result.serialized.value_or(Value{}));
|
||||
auto diff = simpleDiff(data.expected.serialized.value_or(Value{}), data.result.serialized.value_or(Value{}));
|
||||
output.text += "Serialized expression difference:\n"s + diff + "\n"s;
|
||||
}
|
||||
|
||||
if (compileOk && !recompileOk) {
|
||||
auto recompiledValue = toValue(data.recompiled.compiled);
|
||||
auto expectedValue = toValue(data.expected.compiled);
|
||||
output.text += "Recompiled expression difference:\n"s +
|
||||
simpleDiff(recompiledValue, expectedValue) +
|
||||
"\n"s;
|
||||
output.text += "Recompiled expression difference:\n"s + simpleDiff(recompiledValue, expectedValue) + "\n"s;
|
||||
}
|
||||
|
||||
if (compileOk && !evalOk) {
|
||||
auto diff = simpleDiff(data.expected.outputs.value_or(Value{}),
|
||||
data.result.outputs.value_or(Value{}));
|
||||
auto diff = simpleDiff(data.expected.outputs.value_or(Value{}), data.result.outputs.value_or(Value{}));
|
||||
output.text += "Expression outputs difference:\n"s + diff + "\n"s;
|
||||
}
|
||||
|
||||
if (recompileOk && !roundTripOk) {
|
||||
auto diff = simpleDiff(data.expected.outputs.value_or(Value{}),
|
||||
data.recompiled.outputs.value_or(Value{}));
|
||||
output.text += "Roundtripped through serialize expression outputs difference:\n"s +
|
||||
diff + "\n"s;
|
||||
auto diff = simpleDiff(data.expected.outputs.value_or(Value{}), data.recompiled.outputs.value_or(Value{}));
|
||||
output.text += "Roundtripped through serialize expression outputs difference:\n"s + diff + "\n"s;
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
class TestData;
|
||||
|
||||
struct TestRunOutput {
|
||||
TestRunOutput(std::string id_) : id(std::move(id_)) {}
|
||||
TestRunOutput(std::string id_)
|
||||
: id(std::move(id_)) {}
|
||||
std::string id;
|
||||
bool passed = false;
|
||||
std::string text;
|
||||
@@ -17,8 +18,7 @@ struct TestRunOutput {
|
||||
class TestStats {
|
||||
public:
|
||||
std::size_t testCount() const {
|
||||
return passed.size() + failed.size() + errored.size() +
|
||||
ignorePassed.size() + ignoreFailed.size();
|
||||
return passed.size() + failed.size() + errored.size() + ignorePassed.size() + ignoreFailed.size();
|
||||
}
|
||||
|
||||
std::vector<TestRunOutput> passed;
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
int main(int argc, char** argv) try {
|
||||
// Parse args
|
||||
std::vector<mbgl::filesystem::path> testPaths;
|
||||
mbgl::filesystem::path rootPath;
|
||||
@@ -19,7 +18,7 @@ try {
|
||||
|
||||
if (shuffle) {
|
||||
printf(ANSI_COLOR_YELLOW "Shuffle seed: %d" ANSI_COLOR_RESET "\n", seed);
|
||||
std::seed_seq sequence { seed };
|
||||
std::seed_seq sequence{seed};
|
||||
std::mt19937 shuffler(sequence);
|
||||
std::shuffle(testPaths.begin(), testPaths.end(), shuffler);
|
||||
}
|
||||
@@ -28,13 +27,15 @@ try {
|
||||
TestStats stats;
|
||||
for (const auto& path : testPaths) {
|
||||
const auto& expectation = path.parent_path().string();
|
||||
std::string id = expectation.substr(rootPath.string().length() + 1, expectation.length() - rootPath.string().length());
|
||||
std::string id = expectation.substr(rootPath.string().length() + 1,
|
||||
expectation.length() - rootPath.string().length());
|
||||
stats.ids.emplace_back(id);
|
||||
|
||||
bool shouldIgnore = false;
|
||||
std::string ignoreReason;
|
||||
const std::string ignoreName = "expression-tests/" + id;
|
||||
const auto it = std::find_if(ignores.cbegin(), ignores.cend(), [&ignoreName] (const auto& ignore) { return ignore.id == ignoreName; });
|
||||
const auto it = std::find_if(
|
||||
ignores.cbegin(), ignores.cend(), [&ignoreName](const auto& ignore) { return ignore.id == ignoreName; });
|
||||
if (it != ignores.end()) {
|
||||
shouldIgnore = true;
|
||||
ignoreReason = (*it).reason;
|
||||
@@ -54,10 +55,14 @@ try {
|
||||
if (shouldIgnore) {
|
||||
if (testRun->passed) {
|
||||
stats.ignorePassed.emplace_back(std::move(*testRun));
|
||||
printf(ANSI_COLOR_YELLOW "* PASSED ignored test %s (%s)" ANSI_COLOR_RESET "\n", id.c_str(), ignoreReason.c_str());
|
||||
printf(ANSI_COLOR_YELLOW "* PASSED ignored test %s (%s)" ANSI_COLOR_RESET "\n",
|
||||
id.c_str(),
|
||||
ignoreReason.c_str());
|
||||
} else {
|
||||
stats.ignoreFailed.emplace_back(std::move(*testRun));
|
||||
printf(ANSI_COLOR_LIGHT_GRAY "* FAILED ignored test %s (%s)" ANSI_COLOR_RESET "\n", id.c_str(), ignoreReason.c_str());
|
||||
printf(ANSI_COLOR_LIGHT_GRAY "* FAILED ignored test %s (%s)" ANSI_COLOR_RESET "\n",
|
||||
id.c_str(),
|
||||
ignoreReason.c_str());
|
||||
}
|
||||
} else {
|
||||
if (testRun->passed) {
|
||||
@@ -72,10 +77,9 @@ try {
|
||||
|
||||
printStats(stats);
|
||||
writeHTMLResults(stats, rootPath.string(), shuffle, seed);
|
||||
|
||||
|
||||
return stats.errored.size() + stats.failed.size() == 0 ? 0 : 1;
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
} catch (std::exception const& ex) {
|
||||
std::cerr << "Caught an exception while running tests:\n" << ex.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
// Strip precision for numbers, so that we can compare evaluated results with fixtures.
|
||||
// Copied from JS expression harness.
|
||||
// Strip precision for numbers, so that we can compare evaluated results with
|
||||
// fixtures. Copied from JS expression harness.
|
||||
Value stripPrecision(const Value& value) {
|
||||
const double decimalSigFigs = 6;
|
||||
if (auto num = numericValue<double>(value)) {
|
||||
|
||||
Reference in New Issue
Block a user