1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
// 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
|