• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <vector>
6 
7 #include "base/command_line.h"
8 #include "skia/ext/switches.h"
9 #include "third_party/abseil-cpp/absl/types/variant.h"
10 #include "third_party/fuzztest/src/fuzztest/fuzztest.h"
11 #include "third_party/skia/include/core/SkColor.h"
12 #include "ui/gfx/canvas.h"
13 #include "ui/gfx/geometry/size.h"
14 #include "ui/gfx/paint_vector_icon.h"
15 #include "ui/gfx/vector_icon_types.h"
16 #include "ui/gfx/vector_icon_utils.h"
17 
18 namespace {
19 
20 using fuzztest::Arbitrary;
21 using fuzztest::Domain;
22 using fuzztest::ElementOf;
23 using fuzztest::Finite;
24 using fuzztest::FlatMap;
25 using fuzztest::InRange;
26 using fuzztest::Just;
27 using fuzztest::Map;
28 using fuzztest::NonEmpty;
29 using fuzztest::StructOf;
30 using fuzztest::VariantOf;
31 using fuzztest::VectorOf;
32 
33 // Fuzztest does not yet support enums out of the box, but thankfully the
34 // `gfx::CommandType` enum is defined through a pair of macros that work very
35 // well for us. `DECLARE_VECTOR_COMMAND(x)` is supposed to be overridden like
36 // this before `DECLARE_VECTOR_COMMANDS` can be used. Dependency injection!
37 #define DECLARE_VECTOR_COMMAND(x) gfx::CommandType::x,
38 
39 // That allows us to define a domain that contains only valid vector commands.
AnyCommandType()40 auto AnyCommandType() {
41   return ElementOf({DECLARE_VECTOR_COMMANDS});
42 }
43 
44 // Each command type has a specific number of args it expects, otherwise the
45 // command validation code CHECK-fails. We thus make sure to generate only
46 // valid sequences of `gfx::PathElement`s by packaging commands with the right
47 // number of arguments.
48 struct Command {
49   gfx::CommandType type;
50   std::vector<SkScalar> args;
51 };
52 
53 // Returns the domain of all possible arguments for the given command.
54 //
55 // We need this to account for the fact that not all arguments are valid for
56 // all commands, and passing invalid arguments can trigger shallow CHECK
57 // failures that prevent deeper fuzzing.
AnyArgsForCommandType(gfx::CommandType type)58 Domain<std::vector<SkScalar>> AnyArgsForCommandType(gfx::CommandType type) {
59   int args_count = gfx::GetCommandArgumentCount(type);
60   switch (type) {
61     case gfx::PATH_COLOR_ARGB:
62       return VectorOf(InRange(SkScalar(0.0), SkScalar(255.0)))
63           .WithSize(args_count);
64     case gfx::CANVAS_DIMENSIONS:
65       return VectorOf(InRange(SkScalar(1.0), SkScalar(1024.0)))
66           .WithSize(args_count);
67     default:
68       return VectorOf(Finite<SkScalar>()).WithSize(args_count);
69   }
70 }
71 
AnyCommandWithType(gfx::CommandType type)72 Domain<Command> AnyCommandWithType(gfx::CommandType type) {
73   return StructOf<Command>(Just(type), AnyArgsForCommandType(type));
74 }
75 
76 // Returns the domain of all possible commands.
AnyCommand()77 auto AnyCommand() {
78   return FlatMap(AnyCommandWithType, AnyCommandType());
79 }
80 
81 // Flattens the given `commands` into a sequence of path elements that can be
82 // passed to `PaintVectorIcon()`.
ConvertCommands(const std::vector<Command> & commands)83 std::vector<gfx::PathElement> ConvertCommands(
84     const std::vector<Command>& commands) {
85   std::vector<gfx::PathElement> path;
86   for (const auto& command : commands) {
87     path.emplace_back(command.type);
88     for (SkScalar arg : command.args) {
89       path.emplace_back(arg);
90     }
91   }
92   return path;
93 }
94 
95 class PaintVectorIconFuzzTest {
96  public:
PaintVectorIconFuzzTest()97   PaintVectorIconFuzzTest() {
98     // `Init()` ignores its arguments on Windows, so init with nothing and add
99     // switches later.
100     CHECK(base::CommandLine::Init(0, nullptr));
101 
102     // Set command-line arguments correctly to avoid check failures down the
103     // line.
104     base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
105     command_line.AppendSwitchASCII(switches::kTextContrast, "1.0");
106     command_line.AppendSwitchASCII(switches::kTextGamma, "1.0");
107   }
108 
PaintVectorIcon(std::vector<Command> commands)109   void PaintVectorIcon(std::vector<Command> commands) {
110     std::vector<gfx::PathElement> path = ConvertCommands(commands);
111 
112     // An icon can contain multiple representations. We do not fuzz the code
113     // that chooses which representation to draw based on canvas size and scale,
114     // and instead use a single representation.
115     gfx::VectorIconRep rep(path.data(), path.size());
116     gfx::VectorIcon icon(&rep, /*reps_size=*/1u, "icon");
117 
118     constexpr float kImageScale = 1.f;
119     constexpr bool kIsOpaque = true;
120     gfx::Canvas canvas(gfx::Size(1024, 1024), kImageScale, kIsOpaque);
121 
122     // The length of a single edge of the square icon, in device-independent
123     // pixels.
124     constexpr int kDipSize = 1024;
125     constexpr SkColor kBlack = SkColorSetARGB(255, 0, 0, 0);
126     gfx::PaintVectorIcon(&canvas, icon, kDipSize, kBlack);
127   }
128 };
129 
130 FUZZ_TEST_F(PaintVectorIconFuzzTest, PaintVectorIcon)
131     .WithDomains(NonEmpty(VectorOf(AnyCommand())));
132 
133 }  // namespace
134