Skip to content
Open
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
2 changes: 2 additions & 0 deletions doc/domains-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ protocol buffers. Specifically, for the following types:
- Protocol buffer types: `MyProtoMessage`, etc.
- [Abseil time library types](https://abseil.io/docs/cpp/guides/time):
`absl::Duration`, `absl::Time`.
- [Abseil status types](https://abseil.io/docs/cpp/guides/status):
`absl::StatusCode`, `absl::Status`.

Composite or container types, like `std::optional<T>` or `std::vector<T>`, are
supported as long as the inner types are. For example,
Expand Down
1 change: 1 addition & 0 deletions domain_tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ cc_test(
"@abseil-cpp//absl/random",
"@abseil-cpp//absl/random:bit_gen_ref",
"@abseil-cpp//absl/status",
"@abseil-cpp//absl/strings:cord",
"@abseil-cpp//absl/time",
"@com_google_fuzztest//fuzztest:domain_core",
"@com_google_fuzztest//fuzztest/internal:serialization",
Expand Down
74 changes: 74 additions & 0 deletions domain_tests/arbitrary_domains_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "absl/random/bit_gen_ref.h"
#include "absl/random/random.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/time/time.h"
#include "./fuzztest/domain_core.h" // IWYU pragma: keep
#include "./domain_tests/domain_testing.h"
Expand Down Expand Up @@ -626,5 +627,78 @@ TEST(ArbitraryTimeTest, ArbitraryVectorHasAllTypesOfValues) {
EXPECT_THAT(to_find, IsEmpty());
}

TEST(ArbitraryStatusCodeTest, GeneratesAllValues) {
absl::flat_hash_set<absl::StatusCode> to_find = {
absl::StatusCode::kOk,
absl::StatusCode::kCancelled,
absl::StatusCode::kUnknown,
absl::StatusCode::kInvalidArgument,
absl::StatusCode::kDeadlineExceeded,
absl::StatusCode::kNotFound,
absl::StatusCode::kAlreadyExists,
absl::StatusCode::kPermissionDenied,
absl::StatusCode::kResourceExhausted,
absl::StatusCode::kFailedPrecondition,
absl::StatusCode::kAborted,
absl::StatusCode::kOutOfRange,
absl::StatusCode::kUnimplemented,
absl::StatusCode::kInternal,
absl::StatusCode::kUnavailable,
absl::StatusCode::kDataLoss,
absl::StatusCode::kUnauthenticated,
};
auto domain = Arbitrary<absl::StatusCode>();
absl::BitGen prng;

const int max_iterations = IterationsToHitAll(17, 1.0 / (4 * 17));
for (int i = 0; i < max_iterations && !to_find.empty(); ++i) {
to_find.erase(domain.GetRandomValue(prng));
}

EXPECT_THAT(to_find, IsEmpty());
}

TEST(ArbitraryStatusCodeTest, InitGeneratesSeeds) {
Domain<absl::StatusCode> domain = Arbitrary<absl::StatusCode>().WithSeeds(
{absl::StatusCode::kInvalidArgument});

EXPECT_THAT(GenerateInitialValues(domain, 1000),
Contains(Value(domain, absl::StatusCode::kInvalidArgument)));
}

TEST(ArbitraryStatusTest, GeneratesOkAndError) {
auto domain = Arbitrary<absl::Status>();
absl::BitGen prng;
bool found_ok = false;
bool found_error = false;

for (int i = 0; i < 100 && (!found_ok || !found_error); ++i) {
absl::Status s = domain.GetRandomValue(prng);
if (s.ok()) {
found_ok = true;
} else {
found_error = true;
}
}

EXPECT_TRUE(found_ok);
EXPECT_TRUE(found_error);
}

TEST(ArbitraryStatusTest, InitGeneratesSeeds) {
absl::Status seed = absl::InvalidArgumentError("seed message");
Domain<absl::Status> domain = Arbitrary<absl::Status>().WithSeeds({seed});

EXPECT_THAT(GenerateInitialValues(domain, 1000),
Contains(Value(domain, seed)));
}

TEST(ArbitraryStatusTest, FromValueReturnsNulloptWhenPayloadsArePresent) {
auto domain = Arbitrary<absl::Status>();
absl::Status status = absl::InvalidArgumentError("msg");
status.SetPayload("some_url", absl::Cord("payload"));
EXPECT_FALSE(domain.FromValue(status).has_value());
}

} // namespace
} // namespace fuzztest
3 changes: 3 additions & 0 deletions fuzztest/internal/domains/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ cc_library(
name = "absl_helpers",
hdrs = ["absl_helpers.h"],
deps = [
"@abseil-cpp//absl/status",
"@abseil-cpp//absl/strings:cord",
"@abseil-cpp//absl/strings:string_view",
"@abseil-cpp//absl/time",
"@com_google_fuzztest//common:logging",
"@com_google_fuzztest//fuzztest/internal:logging",
Expand Down
10 changes: 10 additions & 0 deletions fuzztest/internal/domains/absl_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include <limits>
#include <utility>

#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "./common/logging.h"
#include "./fuzztest/internal/logging.h"
Expand Down Expand Up @@ -64,6 +67,13 @@ inline uint32_t GetTicks(absl::Duration d) {
return GetSecondsAndTicks(d).second;
}

inline bool HasPayload(const absl::Status& status) {
bool has_payload = false;
status.ForEachPayload(
[&](absl::string_view, const absl::Cord&) { has_payload = true; });
return has_payload;
}

} // namespace fuzztest::internal

#endif // FUZZTEST_FUZZTEST_INTERNAL_DOMAINS_ABSL_HELPERS_H_
59 changes: 59 additions & 0 deletions fuzztest/internal/domains/arbitrary_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
Expand All @@ -31,6 +32,7 @@

#include "absl/random/bit_gen_ref.h"
#include "absl/random/distributions.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "./fuzztest/internal/domains/absl_helpers.h"
Expand Down Expand Up @@ -583,6 +585,63 @@ class ArbitraryImpl<absl::Time>
ArbitraryImpl<absl::Duration>()) {}
};

// Arbitrary for absl::StatusCode.
using StatusCodeUnderlyingT = std::underlying_type_t<absl::StatusCode>;

template <>
class ArbitraryImpl<absl::StatusCode>
: public ReversibleMapImpl<
absl::StatusCode (*)(StatusCodeUnderlyingT),
std::optional<std::tuple<StatusCodeUnderlyingT>> (*)(
absl::StatusCode),
InRangeImpl<StatusCodeUnderlyingT>> {
public:
ArbitraryImpl()
: ReversibleMapImpl<absl::StatusCode (*)(StatusCodeUnderlyingT),
std::optional<std::tuple<StatusCodeUnderlyingT>> (*)(
absl::StatusCode),
InRangeImpl<StatusCodeUnderlyingT>>(
[](StatusCodeUnderlyingT code) {
return static_cast<absl::StatusCode>(code);
},
[](absl::StatusCode code) {
return std::optional{
std::tuple{static_cast<StatusCodeUnderlyingT>(code)}};
},
InRangeImpl<StatusCodeUnderlyingT>(0, 16)) {}
};

// Arbitrary for absl::Status.
// Note: This does not generate payloads. Seeds with payloads will be skipped
// to avoid violating the ReversibleMap invariant (payloads would be stripped).
template <>
class ArbitraryImpl<absl::Status>
: public ReversibleMapImpl<
absl::Status (*)(absl::StatusCode, std::string),
std::optional<std::tuple<absl::StatusCode, std::string>> (*)(
absl::Status),
ArbitraryImpl<absl::StatusCode>, ArbitraryImpl<std::string>> {
public:
ArbitraryImpl()
: ReversibleMapImpl<
absl::Status (*)(absl::StatusCode, std::string),
std::optional<std::tuple<absl::StatusCode, std::string>> (*)(
absl::Status),
ArbitraryImpl<absl::StatusCode>, ArbitraryImpl<std::string>>(
[](absl::StatusCode code, std::string msg) {
return absl::Status(code, msg);
},
+[](absl::Status status)
-> std::optional<std::tuple<absl::StatusCode, std::string>> {
if (HasPayload(status)) {
return std::nullopt;
}
return std::optional{
std::tuple{status.code(), std::string(status.message())}};
},
ArbitraryImpl<absl::StatusCode>(), ArbitraryImpl<std::string>()) {}
};

// Arbitrary for absl::BitGenRef.
template <>
class ArbitraryImpl<absl::BitGenRef>
Expand Down
Loading