blob: 196ab3ed516eb623e201d610ad0652bea05f39d2 [file] [log] [blame]
// 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