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/command_validate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,9 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
"given a single instance"};
}
if (std::filesystem::is_directory(instance_path) ||
instance_path.extension() == ".jsonl") {
instance_path.extension() == ".jsonl" ||
instance_path.extension() == ".yaml" ||
instance_path.extension() == ".yml") {
for (const auto &entry : for_each_json({instance_path_view}, options)) {
std::ostringstream error;
sourcemeta::blaze::SimpleOutput output{entry.second};
Expand Down
49 changes: 47 additions & 2 deletions src/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,59 @@ handle_json_entry(const std::filesystem::path &entry_path,
if (index == 0) {
LOG_WARNING() << "The JSONL file is empty\n";
}
} else if (canonical.extension() == ".yaml" ||
canonical.extension() == ".yml") {
if (std::filesystem::is_empty(canonical)) {
return;
}
auto stream{sourcemeta::core::read_file(canonical)};
std::vector<std::pair<sourcemeta::core::JSON,
sourcemeta::core::PointerPositionTracker>>
documents;
std::uint64_t line_offset{0};
std::uint64_t max_line{0};
while (stream.peek() != std::char_traits<char>::eof()) {
sourcemeta::core::PointerPositionTracker positions;
const std::uint64_t current_offset{line_offset};
max_line = 0;
auto callback = [&positions, current_offset, &max_line](
const sourcemeta::core::JSON::ParsePhase phase,
const sourcemeta::core::JSON::Type type,
const std::uint64_t line,
const std::uint64_t column,
const sourcemeta::core::JSON &value) {
max_line = std::max(max_line, line);
positions(phase, type, line + current_offset, column, value);
};
documents.emplace_back(sourcemeta::core::parse_yaml(stream, callback),
std::move(positions));
// The YAML parser reports the line of the next document separator,
// so we subtract 1 to get the actual lines consumed by this document
line_offset += max_line > 0 ? max_line - 1 : 0;
}

if (documents.size() > 1) {
LOG_VERBOSE(options) << "Interpreting input as YAML multi-document: "
<< canonical.string() << "\n";
std::size_t index{0};
for (auto &entry : documents) {
result.push_back({canonical, std::move(entry.first),
std::move(entry.second), index, true});
index += 1;
}
} else if (documents.size() == 1) {
result.push_back({std::move(canonical),
std::move(documents.front().first),
std::move(documents.front().second)});
}
} else {
if (std::filesystem::is_empty(canonical)) {
return;
}
sourcemeta::core::PointerPositionTracker positions;
// TODO: Print a verbose message for what is getting parsed
auto contents{sourcemeta::core::read_yaml_or_json(canonical,
std::ref(positions))};
auto contents{
sourcemeta::core::read_json(canonical, std::ref(positions))};
result.push_back(
{std::move(canonical), std::move(contents), std::move(positions)});
}
Expand Down
7 changes: 7 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ add_jsonschema_test_unix(validate/pass_many_verbose)
add_jsonschema_test_unix(validate/fail_many)
add_jsonschema_test_unix(validate/fail_many_verbose)
add_jsonschema_test_unix(validate/fail_yaml)
add_jsonschema_test_unix(validate/pass_yaml_multi)
add_jsonschema_test_unix(validate/pass_yaml_multi_verbose)
add_jsonschema_test_unix(validate/pass_yaml_multi_json)
add_jsonschema_test_unix(validate/fail_yaml_multi_one)
add_jsonschema_test_unix(validate/fail_yaml_multi_one_verbose)
add_jsonschema_test_unix(validate/fail_yaml_multi_one_json)
add_jsonschema_test_unix(validate/fail_yaml_multi_blank_lines)
add_jsonschema_test_unix(validate/pass_json_ref_yaml)
add_jsonschema_test_unix(validate/pass_process_substitution)
add_jsonschema_test_unix(validate/pass_2020_12_fast_with_template)
Expand Down
51 changes: 51 additions & 0 deletions test/validate/fail_yaml_multi_blank_lines.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object"
}
EOF

# YAML multi-document with blank lines around separators
cat << 'EOF' > "$TMP/instance.yaml"
---
foo: first

---

- bar: second
---
baz: third
EOF

"$1" validate "$TMP/schema.json" "$TMP/instance.yaml" --verbose 2>"$TMP/stderr.txt" \
&& EXIT_CODE="$?" || EXIT_CODE="$?"
test "$EXIT_CODE" = "2" || exit 1

cat << EOF > "$TMP/expected.txt"
Interpreting input as YAML multi-document: $(realpath "$TMP")/instance.yaml
ok: $(realpath "$TMP")/instance.yaml (entry #1)
matches $(realpath "$TMP")/schema.json
fail: $(realpath "$TMP")/instance.yaml (entry #2)

[
{
"bar": "second"
}
]

error: Schema validation failure
The value was expected to be of type object but it was of type array
at instance location "" (line 6, column 1)
at evaluate path "/type"
EOF

diff "$TMP/stderr.txt" "$TMP/expected.txt"
45 changes: 45 additions & 0 deletions test/validate/fail_yaml_multi_one.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object"
}
EOF

cat << 'EOF' > "$TMP/instance.yaml"
---
foo: 1
---
- foo: 2
---
foo: 3
EOF

"$1" validate "$TMP/schema.json" "$TMP/instance.yaml" 2>"$TMP/stderr.txt" \
&& EXIT_CODE="$?" || EXIT_CODE="$?"
test "$EXIT_CODE" = "2" || exit 1

cat << EOF > "$TMP/expected.txt"
fail: $(realpath "$TMP")/instance.yaml (entry #2)

[
{
"foo": 2
}
]

error: Schema validation failure
The value was expected to be of type object but it was of type array
at instance location "" (line 4, column 1)
at evaluate path "/type"
EOF

diff "$TMP/stderr.txt" "$TMP/expected.txt"
48 changes: 48 additions & 0 deletions test/validate/fail_yaml_multi_one_json.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object"
}
EOF

cat << 'EOF' > "$TMP/instance.yaml"
---
foo: 1
---
- foo: 2
---
foo: 3
EOF

"$1" validate "$TMP/schema.json" "$TMP/instance.yaml" --json > "$TMP/stdout.txt" \
&& EXIT_CODE="$?" || EXIT_CODE="$?"
test "$EXIT_CODE" = "2" || exit 1

cat << EOF > "$TMP/expected.txt"
{
"valid": true
}
{
"valid": false,
"errors": [
{
"keywordLocation": "/type",
"absoluteKeywordLocation": "file://$(realpath "$TMP")/schema.json#/type",
"instanceLocation": "",
"instancePosition": [ 4, 1, 5, 0 ],
"error": "The value was expected to be of type object but it was of type array"
}
]
}
EOF

diff "$TMP/stdout.txt" "$TMP/expected.txt"
48 changes: 48 additions & 0 deletions test/validate/fail_yaml_multi_one_verbose.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object"
}
EOF

cat << 'EOF' > "$TMP/instance.yaml"
---
foo: 1
---
- foo: 2
---
foo: 3
EOF

"$1" validate "$TMP/schema.json" "$TMP/instance.yaml" --verbose 2>"$TMP/stderr.txt" \
&& EXIT_CODE="$?" || EXIT_CODE="$?"
test "$EXIT_CODE" = "2" || exit 1

cat << EOF > "$TMP/expected.txt"
Interpreting input as YAML multi-document: $(realpath "$TMP")/instance.yaml
ok: $(realpath "$TMP")/instance.yaml (entry #1)
matches $(realpath "$TMP")/schema.json
fail: $(realpath "$TMP")/instance.yaml (entry #2)

[
{
"foo": 2
}
]

error: Schema validation failure
The value was expected to be of type object but it was of type array
at instance location "" (line 4, column 1)
at evaluate path "/type"
EOF

diff "$TMP/stderr.txt" "$TMP/expected.txt"
35 changes: 35 additions & 0 deletions test/validate/pass_yaml_multi.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"foo": {
"type": "string"
}
}
}
EOF

cat << 'EOF' > "$TMP/instance.yaml"
---
foo: first
---
foo: second
---
foo: third
EOF

"$1" validate "$TMP/schema.json" "$TMP/instance.yaml" 2> "$TMP/output.txt" 1>&2

cat << EOF > "$TMP/expected.txt"
EOF

diff "$TMP/output.txt" "$TMP/expected.txt"
44 changes: 44 additions & 0 deletions test/validate/pass_yaml_multi_json.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"foo": {
"type": "string"
}
}
}
EOF

cat << 'EOF' > "$TMP/instance.yaml"
---
foo: first
---
foo: second
---
foo: third
EOF

"$1" validate "$TMP/schema.json" "$TMP/instance.yaml" --json > "$TMP/output.json" 2>&1

cat << EOF > "$TMP/expected.json"
{
"valid": true
}
{
"valid": true
}
{
"valid": true
}
EOF

diff "$TMP/output.json" "$TMP/expected.json"
Loading