Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/linter/include/sourcemeta/blaze/linter.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#include <sourcemeta/blaze/compiler.h>
#include <sourcemeta/core/jsonschema.h>

#include <cstddef> // std::size_t
#include <set> // std::set

/// @defgroup linter Linter
/// @brief A set of JSON Schema linter extensions powered by Blaze
///
Expand Down Expand Up @@ -45,6 +48,7 @@ class SOURCEMETA_BLAZE_LINTER_EXPORT ValidExamples final
#pragma warning(disable : 4251)
#endif
const Compiler compiler_;
mutable std::set<std::size_t> invalid_indices_;
#if defined(_MSC_VER)
#pragma warning(default : 4251)
#endif
Expand Down
40 changes: 34 additions & 6 deletions src/linter/valid_examples.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <cstddef> // std::size_t
#include <functional> // std::ref, std::cref
#include <set> // std::set
#include <sstream> // std::ostringstream
#include <utility> // std::move

Expand All @@ -20,7 +21,7 @@ ValidExamples::ValidExamples(Compiler compiler)
SchemaTransformRule{"blaze/valid_examples",
"Only include instances in the `examples` array "
"that validate against the schema"},
compiler_{std::move(compiler)} {};
compiler_{std::move(compiler)}, invalid_indices_{} {};

auto ValidExamples::condition(
const sourcemeta::core::JSON &schema, const sourcemeta::core::JSON &root,
Expand Down Expand Up @@ -73,26 +74,53 @@ auto ValidExamples::condition(

Evaluator evaluator;
std::size_t cursor{0};
this->invalid_indices_.clear();
std::ostringstream collected_messages;

for (const auto &example : schema.at("examples").as_array()) {
const std::string ref{"$ref"};
SimpleOutput output{example, {std::cref(ref)}};
const auto result{
evaluator.validate(schema_template, example, std::ref(output))};
if (!result) {
std::ostringstream message;
message << "Invalid example instance at index " << cursor << "\n";
output.stacktrace(message, " ");
return message.str();
this->invalid_indices_.insert(cursor);
collected_messages << "Invalid example instance at index " << cursor
<< "\n";
output.stacktrace(collected_messages, " ");
}

cursor += 1;
}

if (!this->invalid_indices_.empty()) {
return collected_messages.str();
}

return false;
}

auto ValidExamples::transform(sourcemeta::core::JSON &schema) const -> void {
schema.erase("examples");
if (!schema.is_object() || !schema.defines("examples") ||
!schema.at("examples").is_array() || this->invalid_indices_.empty()) {
return;
}

auto &examples_array = schema.at("examples");
auto new_examples = sourcemeta::core::JSON::make_array();

std::size_t index = 0;
for (const auto &example : examples_array.as_array()) {
if (this->invalid_indices_.find(index) == this->invalid_indices_.end()) {
new_examples.push_back(example);
}
index++;
}

if (new_examples.empty()) {
schema.erase("examples");
} else {
schema.assign("examples", std::move(new_examples));
}
}

} // namespace sourcemeta::blaze
62 changes: 58 additions & 4 deletions test/linter/linter_valid_examples_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ TEST(Linter, valid_examples_3) {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"foo": {
"type": "string"
"type": "string",
"examples": [ "foo", "baz" ]
}
}
})JSON")};
Expand Down Expand Up @@ -296,7 +297,8 @@ TEST(Linter, valid_examples_4) {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"properties": {
"foo": {
"type": "string"
"type": "string",
"examples": [ "foo", "baz" ]
}
}
})JSON")};
Expand Down Expand Up @@ -330,7 +332,8 @@ TEST(Linter, valid_examples_5) {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"foo": {
"type": "string"
"type": "string",
"examples": [ "foo", "baz" ]
}
}
})JSON")};
Expand Down Expand Up @@ -364,7 +367,8 @@ TEST(Linter, valid_examples_6) {
"$schema": "http://json-schema.org/draft-06/schema#",
"properties": {
"foo": {
"type": "string"
"type": "string",
"examples": [ "foo", "baz" ]
}
}
})JSON")};
Expand Down Expand Up @@ -683,3 +687,53 @@ TEST(Linter, valid_examples_15) {

EXPECT_EQ(schema, expected);
}
TEST(Linter, valid_examples_mixed_valid_invalid) {
sourcemeta::core::SchemaTransformer bundle;
bundle.add<sourcemeta::blaze::ValidExamples>(
sourcemeta::blaze::default_schema_compiler);

auto schema{sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"examples": [ "valid1", 123, "valid2", true, "valid3" ]
})JSON")};

const auto result = bundle.apply(
schema, sourcemeta::core::schema_official_walker,
sourcemeta::core::schema_official_resolver, transformer_callback_error);

EXPECT_TRUE(result);

const auto expected{sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"examples": [ "valid1", "valid2", "valid3" ]
})JSON")};

EXPECT_EQ(schema, expected);
}

TEST(Linter, valid_examples_all_invalid_removes_property) {
sourcemeta::core::SchemaTransformer bundle;
bundle.add<sourcemeta::blaze::ValidExamples>(
sourcemeta::blaze::default_schema_compiler);

auto schema{sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"examples": [ 123, true, 456 ]
})JSON")};

const auto result = bundle.apply(
schema, sourcemeta::core::schema_official_walker,
sourcemeta::core::schema_official_resolver, transformer_callback_error);

EXPECT_TRUE(result);

const auto expected{sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
})JSON")};

EXPECT_EQ(schema, expected);
}
Loading