blob: e34f9f19304a8cd5d6e155d72379589d7a774dad [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fstream>
#include <fidl/findings_json.h>
#include <fidl/template_string.h>
#include <fidl/utils.h>
#include "test_library.h"
#include "unittest_helpers.h"
namespace fidl {
namespace {
#define ASSERT_JSON(TEST, JSON) \
ASSERT_NO_FATAL_FAILURES(TEST.ExpectJson(JSON)); \
TEST.Reset()
void FindingsEmitThisJson(Findings& findings, std::string expected_json) {
std::string actual_json = fidl::FindingsJson(findings).Produce().str();
if (expected_json != actual_json) {
std::ofstream output_actual("json_findings_tests_actual.txt");
output_actual << actual_json;
output_actual.close();
std::ofstream output_expected("json_findings_tests_expected.txt");
output_expected << expected_json;
output_expected.close();
}
EXPECT_STRING_EQ(
expected_json, actual_json,
"To compare results, run:\n\n diff ./json_findings_tests_{expected,actual}.txt\n");
}
class JsonFindingsTest {
public:
JsonFindingsTest(std::string filename, std::string source) : default_filename_(filename) {
AddSourceFile(filename, source);
}
void AddSourceFile(std::string filename, std::string source) {
sources_.emplace(filename, SourceFile(filename, source));
}
struct AddFindingArgs {
std::string filename;
std::string check_id;
std::string message;
std::string violation_string;
// If the intended violation_string is too short to match a unique pattern,
// set the violation_string to the string that is long enough, and set
// |forced_size| to the desired length of the |std::string_view| at that
// location.
size_t forced_size = std::string::npos;
};
// Note: |line| and |column| are 1-based
Finding* AddFinding(AddFindingArgs args) {
auto filename = args.filename;
if (filename.empty()) {
filename = default_filename_;
}
auto result = sources_.find(filename);
assert(result != sources_.end());
auto& source_file = result->second;
std::string_view source_data = source_file.data();
size_t start = source_data.find(args.violation_string);
size_t size = args.violation_string.size();
if (args.forced_size != std::string::npos) {
size = args.forced_size;
}
if (start == std::string::npos) {
std::cout << "ERROR: violation_string '" << args.violation_string
<< "' was not found in template string:" << std::endl
<< source_data;
}
assert(start != std::string::npos && "Bad test! violation_string not found in source data");
source_data.remove_prefix(start);
source_data.remove_suffix(source_data.size() - size);
auto span = fidl::SourceSpan(source_data, source_file);
return &findings_.emplace_back(span, args.check_id, args.message);
}
void ExpectJson(std::string expected_json) { FindingsEmitThisJson(findings_, expected_json); }
void Reset() { findings_.clear(); }
private:
std::string default_filename_;
std::map<std::string, SourceFile> sources_;
Findings findings_;
};
TEST(JsonFindingsTests, simple_finding) {
auto test = JsonFindingsTest("simple_finding_test_file", R"ANYLANG(Findings are
language
agnostic.
)ANYLANG");
test.AddFinding(
{.check_id = "check-1", .message = "Finding message", .violation_string = "Findings"});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple_finding_test_file",
"start_line": 1,
"start_char": 0,
"end_line": 1,
"end_char": 8,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, simple_fidl) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding({.check_id = "on-ward-check",
.message = "OnWard seems like a silly name for an event",
.violation_string = "OnWard"});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/on-ward-check",
"message": "OnWard seems like a silly name for an event",
"path": "simple.fidl",
"start_line": 5,
"start_char": 5,
"end_line": 5,
"end_char": 11,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, zero_length_string) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding({.check_id = "check-1",
.message = "Finding message",
.violation_string = "OnWard",
.forced_size = 0});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple.fidl",
"start_line": 5,
"start_char": 5,
"end_line": 5,
"end_char": 5,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, starts_on_newline) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding(
{.check_id = "check-1", .message = "Finding message", .violation_string = "\nlibrary"});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple.fidl",
"start_line": 1,
"start_char": 0,
"end_line": 2,
"end_char": 7,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, ends_on_newline) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding({.check_id = "check-1",
.message = "Finding message",
.violation_string = "TestProtocol {\n"});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple.fidl",
"start_line": 4,
"start_char": 9,
"end_line": 5,
"end_char": 0,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, ends_on_eof) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding(
{.check_id = "check-1", .message = "Finding message", .violation_string = "};\n"});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple.fidl",
"start_line": 6,
"start_char": 0,
"end_line": 6,
"end_char": 2,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, finding_with_suggestion_no_replacement) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding(
{.check_id = "check-1", .message = "Finding message", .violation_string = "TestProtocol"})
->SetSuggestion("Suggestion description");
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple.fidl",
"start_line": 4,
"start_char": 9,
"end_line": 4,
"end_char": 21,
"suggestions": [
{
"description": "Suggestion description",
"replacements": []
}
]
}
])JSON");
}
TEST(JsonFindingsTests, finding_with_replacement) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding(
{.check_id = "check-1", .message = "Finding message", .violation_string = "TestProtocol"})
->SetSuggestion("Suggestion description", "BestProtocol");
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple.fidl",
"start_line": 4,
"start_char": 9,
"end_line": 4,
"end_char": 21,
"suggestions": [
{
"description": "Suggestion description",
"replacements": [
{
"replacement": "BestProtocol",
"path": "simple.fidl",
"start_line": 4,
"start_char": 9,
"end_line": 4,
"end_char": 21
}
]
}
]
}
])JSON");
}
TEST(JsonFindingsTests, finding_spans_2_lines) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol
TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding({.check_id = "check-1",
.message = "Finding message",
.violation_string = "protocol\n TestProtocol"});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple.fidl",
"start_line": 4,
"start_char": 0,
"end_line": 5,
"end_char": 13,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, two_findings) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding(
{.check_id = "check-1", .message = "Finding message", .violation_string = "TestProtocol"});
test.AddFinding(
{.check_id = "check-2", .message = "Finding message 2", .violation_string = "OnWard"});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple.fidl",
"start_line": 4,
"start_char": 9,
"end_line": 4,
"end_char": 21,
"suggestions": []
},
{
"category": "fidl-lint/check-2",
"message": "Finding message 2",
"path": "simple.fidl",
"start_line": 5,
"start_char": 5,
"end_line": 5,
"end_char": 11,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, three_findings) {
auto test = JsonFindingsTest("simple.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddFinding(
{.check_id = "check-3", .message = "Finding message 3", .violation_string = "library"});
test.AddFinding(
{.check_id = "check-4", .message = "Finding message 4", .violation_string = "fidl.a"})
->SetSuggestion("Suggestion description");
test.AddFinding({.check_id = "check-5", .message = "Finding message 5", .violation_string = "->"})
->SetSuggestion("Suggestion description for finding 5", "Replacement string for finding 5");
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-3",
"message": "Finding message 3",
"path": "simple.fidl",
"start_line": 2,
"start_char": 0,
"end_line": 2,
"end_char": 7,
"suggestions": []
},
{
"category": "fidl-lint/check-4",
"message": "Finding message 4",
"path": "simple.fidl",
"start_line": 2,
"start_char": 8,
"end_line": 2,
"end_char": 14,
"suggestions": [
{
"description": "Suggestion description",
"replacements": []
}
]
},
{
"category": "fidl-lint/check-5",
"message": "Finding message 5",
"path": "simple.fidl",
"start_line": 5,
"start_char": 2,
"end_line": 5,
"end_char": 4,
"suggestions": [
{
"description": "Suggestion description for finding 5",
"replacements": [
{
"replacement": "Replacement string for finding 5",
"path": "simple.fidl",
"start_line": 5,
"start_char": 2,
"end_line": 5,
"end_char": 4
}
]
}
]
}
])JSON");
}
TEST(JsonFindingsTests, multiple_files) {
auto test = JsonFindingsTest("simple_1.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> OnWard();
};
)FIDL");
test.AddSourceFile("simple_2.fidl", R"FIDL(
library fidl.b;
struct TestStruct {
string field;
};
)FIDL");
test.AddFinding({.filename = "simple_1.fidl",
.check_id = "check-1",
.message = "Finding message",
.violation_string = "TestProtocol"});
test.AddFinding({.filename = "simple_2.fidl",
.check_id = "check-2",
.message = "Finding message 2",
.violation_string = "field"});
ASSERT_JSON(test, R"JSON([
{
"category": "fidl-lint/check-1",
"message": "Finding message",
"path": "simple_1.fidl",
"start_line": 4,
"start_char": 9,
"end_line": 4,
"end_char": 21,
"suggestions": []
},
{
"category": "fidl-lint/check-2",
"message": "Finding message 2",
"path": "simple_2.fidl",
"start_line": 5,
"start_char": 9,
"end_line": 5,
"end_char": 14,
"suggestions": []
}
])JSON");
}
TEST(JsonFindingsTests, fidl_json_end_to_end) {
auto library = std::make_unique<TestLibrary>("example.fidl", R"FIDL(
library fidl.a;
protocol TestProtocol {
-> Press();
};
)FIDL");
Findings findings;
ASSERT_FALSE(library->Lint(&findings));
ASSERT_NO_FATAL_FAILURES(FindingsEmitThisJson(findings, R"JSON([
{
"category": "fidl-lint/event-names-must-start-with-on",
"message": "Event names must start with 'On'",
"path": "example.fidl",
"start_line": 5,
"start_char": 5,
"end_line": 5,
"end_char": 10,
"suggestions": [
{
"description": "change 'Press' to 'OnPress'",
"replacements": [
{
"replacement": "OnPress",
"path": "example.fidl",
"start_line": 5,
"start_char": 5,
"end_line": 5,
"end_char": 10
}
]
}
]
}
])JSON"));
}
} // namespace
} // namespace fidl