Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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: 3 additions & 1 deletion src/linter/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME linter
FOLDER "Blaze/Linter"
SOURCES valid_examples.cc)
SOURCES
valid_default.cc
valid_examples.cc)

if(BLAZE_INSTALL)
sourcemeta_library_install(NAMESPACE sourcemeta PROJECT blaze NAME linter)
Expand Down
31 changes: 31 additions & 0 deletions src/linter/include/sourcemeta/blaze/linter.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,37 @@ class SOURCEMETA_BLAZE_LINTER_EXPORT ValidExamples final
#endif
};

/// @ingroup linter
///
/// Check that the `default` keyword consists of an instance that matches the
/// corresponding schema.
class SOURCEMETA_BLAZE_LINTER_EXPORT ValidDefault final
: public sourcemeta::core::SchemaTransformRule {
public:
ValidDefault(Compiler compiler);
[[nodiscard]] auto condition(const sourcemeta::core::JSON &,
const sourcemeta::core::JSON &,
const sourcemeta::core::Vocabularies &,
const sourcemeta::core::SchemaFrame &,
const sourcemeta::core::SchemaFrame::Location &,
const sourcemeta::core::SchemaWalker &,
const sourcemeta::core::SchemaResolver &) const
-> bool override;
auto transform(sourcemeta::core::JSON &) const -> void override;

private:
// Exporting symbols that depends on the standard C++ library is considered
// safe.
// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN
#if defined(_MSC_VER)
#pragma warning(disable : 4251)
#endif
const Compiler compiler_;
#if defined(_MSC_VER)
#pragma warning(default : 4251)
#endif
};

} // namespace sourcemeta::blaze

#endif
49 changes: 49 additions & 0 deletions src/linter/valid_default.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <sourcemeta/blaze/evaluator.h>
#include <sourcemeta/blaze/linter.h>

namespace sourcemeta::blaze {

ValidDefault::ValidDefault(Compiler compiler)
: sourcemeta::core::SchemaTransformRule{"blaze/valid_default",
"Only set a `default` valid that "
"validates against the schema"},
compiler_{std::move(compiler)} {};

auto ValidDefault::condition(
const sourcemeta::core::JSON &schema, const sourcemeta::core::JSON &root,
const sourcemeta::core::Vocabularies &vocabularies,
const sourcemeta::core::SchemaFrame &,
const sourcemeta::core::SchemaFrame::Location &location,
const sourcemeta::core::SchemaWalker &walker,
const sourcemeta::core::SchemaResolver &resolver) const -> bool {
// Technically, the `default` keyword goes back to Draft 1, but Blaze
// only supports Draft 4 and later
if (!vocabularies.contains(
"https://json-schema.org/draft/2020-12/vocab/meta-data") &&
!vocabularies.contains(
"https://json-schema.org/draft/2019-09/vocab/meta-data") &&
!vocabularies.contains("http://json-schema.org/draft-07/schema#") &&
!vocabularies.contains("http://json-schema.org/draft-06/schema#") &&
!vocabularies.contains("http://json-schema.org/draft-04/schema#")) {
return false;
}

if (!schema.defines("default")) {
return false;
}

const auto subschema{sourcemeta::core::wrap(root, location.pointer, resolver,
location.dialect)};
const auto schema_template{compile(subschema, walker, resolver,
this->compiler_, Mode::FastValidation,
location.dialect)};

Evaluator evaluator;
return !evaluator.validate(schema_template, schema.at("default"));
}

auto ValidDefault::transform(sourcemeta::core::JSON &schema) const -> void {
schema.erase("default");
}

} // namespace sourcemeta::blaze
4 changes: 3 additions & 1 deletion test/linter/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
sourcemeta_googletest(NAMESPACE sourcemeta PROJECT blaze NAME linter
FOLDER "Blaze/Linter"
SOURCES linter_valid_examples_test.cc)
SOURCES
linter_valid_default_test.cc
linter_valid_examples_test.cc)

target_link_libraries(sourcemeta_blaze_linter_unit
PRIVATE sourcemeta::core::json)
Expand Down
236 changes: 236 additions & 0 deletions test/linter/linter_valid_default_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#include <gtest/gtest.h>

#include <sourcemeta/blaze/linter.h>

#include <sourcemeta/core/json.h>
#include <sourcemeta/core/jsonschema.h>

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

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

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

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);
}

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

auto schema{sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": { "foo": "bar" },
"properties": {
"foo": {
"type": "string",
"default": "baz"
}
}
})JSON")};

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

EXPECT_TRUE(result);

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

EXPECT_EQ(schema, expected);
}

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

auto schema{sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": { "foo": 1 },
"properties": {
"foo": {
"type": "string",
"default": true
}
}
})JSON")};

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

EXPECT_TRUE(result);

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

EXPECT_EQ(schema, expected);
}

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

auto schema{sourcemeta::core::parse_json(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"default": { "foo": 1 },
"properties": {
"foo": {
"type": "string",
"default": true
}
}
})JSON")};

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

EXPECT_TRUE(result);

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

EXPECT_EQ(schema, expected);
}

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

auto schema{sourcemeta::core::parse_json(R"JSON({
"$schema": "http://json-schema.org/draft-07/schema#",
"default": { "foo": 1 },
"properties": {
"foo": {
"type": "string",
"default": true
}
}
})JSON")};

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

EXPECT_TRUE(result);

const auto expected{sourcemeta::core::parse_json(R"JSON({
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"foo": {
"type": "string"
}
}
})JSON")};

EXPECT_EQ(schema, expected);
}

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

auto schema{sourcemeta::core::parse_json(R"JSON({
"$schema": "http://json-schema.org/draft-06/schema#",
"default": { "foo": 1 },
"properties": {
"foo": {
"type": "string",
"default": true
}
}
})JSON")};

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

EXPECT_TRUE(result);

const auto expected{sourcemeta::core::parse_json(R"JSON({
"$schema": "http://json-schema.org/draft-06/schema#",
"properties": {
"foo": {
"type": "string"
}
}
})JSON")};

EXPECT_EQ(schema, expected);
}

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

auto schema{sourcemeta::core::parse_json(R"JSON({
"$schema": "http://json-schema.org/draft-04/schema#",
"default": { "foo": 1 },
"properties": {
"foo": {
"type": "string",
"default": true
}
}
})JSON")};

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

EXPECT_TRUE(result);

const auto expected{sourcemeta::core::parse_json(R"JSON({
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"foo": {
"type": "string"
}
}
})JSON")};

EXPECT_EQ(schema, expected);
}