You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
5.0 KiB
143 lines
5.0 KiB
// Copyright 2022 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Tests of domain `InJsonGrammar`, the one provided example of an InGrammar
|
|
// domain.
|
|
|
|
#include <string>
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "absl/container/flat_hash_set.h"
|
|
#include "absl/random/random.h"
|
|
#include "./fuzztest/domain.h" // IWYU pragma: keep
|
|
#include "./domain_tests/domain_testing.h"
|
|
#include "./fuzztest/grammars/json_grammar.h"
|
|
#include "./fuzztest/internal/serialization.h"
|
|
#include "nlohmann/json.hpp"
|
|
|
|
namespace fuzztest {
|
|
namespace {
|
|
|
|
TEST(InJsonGrammar, InitGeneratesDifferentValidJson) {
|
|
absl::BitGen bitgen;
|
|
auto domain = InJsonGrammar();
|
|
absl::flat_hash_set<std::string> valid_jsons;
|
|
|
|
while (valid_jsons.size() < 1000) {
|
|
Value val(domain, bitgen);
|
|
nlohmann::json parsed_json = nlohmann::json::parse(
|
|
val.user_value.begin(), val.user_value.end(), nullptr, false);
|
|
EXPECT_FALSE(parsed_json.is_discarded()) << val;
|
|
valid_jsons.insert(parsed_json.dump());
|
|
}
|
|
}
|
|
|
|
TEST(InJsonGrammar, InitGeneratesShortJson) {
|
|
absl::BitGen bitgen;
|
|
auto domain = InJsonGrammar();
|
|
|
|
size_t total_ast_node_num = 0;
|
|
const size_t total_generation_num = 10000;
|
|
for (size_t i = 0; i < total_generation_num; ++i) {
|
|
Value val(domain, bitgen);
|
|
total_ast_node_num += val.corpus_value.NodeCount();
|
|
}
|
|
|
|
// The AST generation tries to generate small ASTs. For example, it will only
|
|
// generate one or zero element for a rule `list: element*`. For the current
|
|
// json grammar, the average size (the number of ast nodes) of an ast is 7.
|
|
// TODO(changochen): Say more about the AST size/depth/shape.
|
|
const size_t average_ast_size = total_ast_node_num / total_generation_num;
|
|
EXPECT_NEAR(average_ast_size, 10, 1);
|
|
}
|
|
|
|
TEST(InJsonGrammar, MutateGeneratesValidValues) {
|
|
absl::BitGen bitgen;
|
|
auto domain = InJsonGrammar();
|
|
absl::flat_hash_set<std::string> valid_jsons;
|
|
Value ast(domain, bitgen);
|
|
|
|
while (valid_jsons.size() < 5000) {
|
|
ast.Mutate(domain, bitgen, {}, false);
|
|
nlohmann::json parsed_json = nlohmann::json::parse(
|
|
ast.user_value.begin(), ast.user_value.end(), nullptr, false);
|
|
EXPECT_FALSE(parsed_json.is_discarded());
|
|
valid_jsons.insert(parsed_json.dump());
|
|
}
|
|
}
|
|
|
|
TEST(InJsonGrammar, MutateGeneratesDifferentValuesWithHighProb) {
|
|
absl::BitGen bitgen;
|
|
auto domain = InJsonGrammar();
|
|
absl::flat_hash_set<std::string> valid_jsons;
|
|
int num_real_mutation = 0, num_total_mutation = 0;
|
|
Value ast(domain, bitgen);
|
|
nlohmann::json previous_val = nlohmann::json::parse(
|
|
ast.user_value.begin(), ast.user_value.end(), nullptr, false);
|
|
|
|
while (valid_jsons.size() < 5000) {
|
|
++num_total_mutation;
|
|
ast.Mutate(domain, bitgen, {}, false);
|
|
nlohmann::json parsed_json = nlohmann::json::parse(
|
|
ast.user_value.begin(), ast.user_value.end(), nullptr, false);
|
|
valid_jsons.insert(parsed_json.dump());
|
|
|
|
// There might be chances that the mutated AST is the same if because
|
|
// mutating regexp doesn't guarantee changes. Otherwise, it guarantees real
|
|
// mutation.
|
|
if (parsed_json != previous_val) ++num_real_mutation;
|
|
previous_val = parsed_json;
|
|
}
|
|
const double probability_of_real_mutation =
|
|
static_cast<double>(num_real_mutation) / num_total_mutation;
|
|
EXPECT_NEAR(probability_of_real_mutation, 0.99, 0.015);
|
|
}
|
|
|
|
TEST(InJsonGrammar, ShrinkModeReducesInputSize) {
|
|
absl::BitGen bitgen;
|
|
auto domain = InJsonGrammar();
|
|
int num_of_successful_shrink = 0;
|
|
int expected_num_of_successful_shrink = 0;
|
|
for (int i = 0; i < 100; ++i) {
|
|
Value val(domain, bitgen);
|
|
size_t original_size = val.corpus_value.NodeCount();
|
|
// If the generated input is big, we expect it to be shrinkable.
|
|
if (original_size > 20) ++expected_num_of_successful_shrink;
|
|
|
|
for (int j = 0; j < 10; ++j) {
|
|
val.Mutate(domain, bitgen, {}, true);
|
|
EXPECT_TRUE(val.corpus_value.NodeCount() <= original_size);
|
|
}
|
|
if (original_size > val.corpus_value.NodeCount()) {
|
|
++num_of_successful_shrink;
|
|
}
|
|
}
|
|
EXPECT_GE(num_of_successful_shrink, expected_num_of_successful_shrink);
|
|
}
|
|
|
|
TEST(InTestGrammar, InGrammarCorpusSerializesCorpusAndParsesCorpusCorrectly) {
|
|
absl::BitGen bitgen;
|
|
auto domain = InJsonGrammar();
|
|
for (int i = 0; i < 1000; ++i) {
|
|
Value val(domain, bitgen);
|
|
std::cout << domain.GetValue(val.corpus_value) << std::endl;
|
|
auto ir_object = domain.SerializeCorpus(val.corpus_value);
|
|
auto ast = domain.ParseCorpus(ir_object);
|
|
ASSERT_TRUE(ast.has_value());
|
|
EXPECT_EQ(domain.GetValue(*ast), val.user_value);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace fuzztest
|
|
|