blob: 68f5554a050a27f69be40d1cadcabfd2f6c5a72f [file] [log] [blame] [edit]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <vector>
#include "base/command_line.h"
#include "skia/ext/switches.h"
#include "third_party/fuzztest/src/fuzztest/fuzztest.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/gfx/vector_icon_utils.h"
namespace {
using fuzztest::Arbitrary;
using fuzztest::Domain;
using fuzztest::ElementOf;
using fuzztest::Finite;
using fuzztest::FlatMap;
using fuzztest::InRange;
using fuzztest::Just;
using fuzztest::Map;
using fuzztest::NonEmpty;
using fuzztest::StructOf;
using fuzztest::VariantOf;
using fuzztest::VectorOf;
// Fuzztest does not yet support enums out of the box, but thankfully the
// `gfx::CommandType` enum is defined through a pair of macros that work very
// well for us. `DECLARE_VECTOR_COMMAND(x)` is supposed to be overridden like
// this before `DECLARE_VECTOR_COMMANDS` can be used. Dependency injection!
#define DECLARE_VECTOR_COMMAND(x) gfx::CommandType::x,
// That allows us to define a domain that contains only valid vector commands.
auto AnyCommandType() {
return ElementOf({DECLARE_VECTOR_COMMANDS});
}
// Each command type has a specific number of args it expects, otherwise the
// command validation code CHECK-fails. We thus make sure to generate only
// valid sequences of `gfx::PathElement`s by packaging commands with the right
// number of arguments.
struct Command {
gfx::CommandType type;
std::vector<SkScalar> args;
};
// Returns the domain of all possible arguments for the given command.
//
// We need this to account for the fact that not all arguments are valid for
// all commands, and passing invalid arguments can trigger shallow CHECK
// failures that prevent deeper fuzzing.
Domain<std::vector<SkScalar>> AnyArgsForCommandType(gfx::CommandType type) {
int args_count = gfx::GetCommandArgumentCount(type);
switch (type) {
case gfx::PATH_COLOR_ARGB:
return VectorOf(InRange(SkScalar(0.0), SkScalar(255.0)))
.WithSize(args_count);
case gfx::CANVAS_DIMENSIONS:
return VectorOf(InRange(SkScalar(1.0), SkScalar(1024.0)))
.WithSize(args_count);
default:
return VectorOf(Finite<SkScalar>()).WithSize(args_count);
}
}
Domain<Command> AnyCommandWithType(gfx::CommandType type) {
return StructOf<Command>(Just(type), AnyArgsForCommandType(type));
}
// Returns the domain of all possible commands.
auto AnyCommand() {
return FlatMap(AnyCommandWithType, AnyCommandType());
}
// Flattens the given `commands` into a sequence of path elements that can be
// passed to `PaintVectorIcon()`.
std::vector<gfx::PathElement> ConvertCommands(
const std::vector<Command>& commands) {
std::vector<gfx::PathElement> path;
for (const auto& command : commands) {
path.emplace_back(command.type);
for (SkScalar arg : command.args) {
path.emplace_back(arg);
}
}
return path;
}
class PaintVectorIconFuzzTest {
public:
PaintVectorIconFuzzTest() {
// `Init()` ignores its arguments on Windows, so init with nothing and add
// switches later.
CHECK(base::CommandLine::Init(0, nullptr));
// Set command-line arguments correctly to avoid check failures down the
// line.
base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
command_line.AppendSwitchASCII(switches::kTextContrast, "1.0");
command_line.AppendSwitchASCII(switches::kTextGamma, "1.0");
}
void PaintVectorIcon(std::vector<Command> commands) {
std::vector<gfx::PathElement> path = ConvertCommands(commands);
// An icon can contain multiple representations. We do not fuzz the code
// that chooses which representation to draw based on canvas size and scale,
// and instead use a single representation.
gfx::VectorIconRep rep{path};
gfx::VectorIcon icon(&rep, /*reps_size=*/1u, "icon");
constexpr float kImageScale = 1.f;
constexpr bool kIsOpaque = true;
gfx::Canvas canvas(gfx::Size(1024, 1024), kImageScale, kIsOpaque);
// The length of a single edge of the square icon, in device-independent
// pixels.
constexpr int kDipSize = 1024;
constexpr SkColor kBlack = SkColorSetARGB(255, 0, 0, 0);
gfx::PaintVectorIcon(&canvas, icon, kDipSize, kBlack);
}
};
FUZZ_TEST_F(PaintVectorIconFuzzTest, PaintVectorIcon)
.WithDomains(NonEmpty(VectorOf(AnyCommand())));
} // namespace