Flutter Engine
The Flutter Engine
FuzzSkParagraph.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "fuzz/Fuzz.h"
9#include "fuzz/FuzzCommon.h"
17#include "include/core/SkRect.h"
20#include "include/core/SkSpan.h"
39#include "src/core/SkOSFile.h"
40#include "src/utils/SkOSPath.h"
41#include "tests/Test.h"
42#include "tools/Resources.h"
44
45#include <string.h>
46#include <algorithm>
47#include <limits>
48#include <memory>
49#include <string>
50#include <utility>
51#include <vector>
52
53#if defined(SK_ENABLE_PARAGRAPH)
54
55using namespace skia::textlayout;
56namespace {
57const uint8_t MAX_TEXT_LENGTH = 255;
58const uint8_t MAX_TEXT_ADDITIONS = 4;
59// Use 250 so uint8 can create text and layout width larger than the canvas.
60const uint16_t TEST_CANVAS_DIM = 250;
61
62class ResourceFontCollection : public FontCollection {
63public:
64 ResourceFontCollection(bool testOnly = false)
65 : fFontsFound(false)
66 , fResolvedFonts(0)
67 , fResourceDir(GetResourcePath("fonts").c_str())
68 , fFontProvider(sk_make_sp<TypefaceFontProvider>()) {
69 std::vector<SkString> fonts;
70 SkOSFile::Iter iter(fResourceDir.c_str());
71
74 while (iter.next(&path)) {
75 if (path.endsWith("Roboto-Italic.ttf")) {
76 fFontsFound = true;
77 }
78 fonts.emplace_back(path);
79 }
80
81 if (!fFontsFound) {
82 // SkDebugf("Fonts not found, skipping all the tests\n");
83 return;
84 }
85 // Only register fonts if we have to
86 for (auto& font : fonts) {
87 SkString file_path;
88 file_path.printf("%s/%s", fResourceDir.c_str(), font.c_str());
89 fFontProvider->registerTypeface(mgr->makeFromFile(file_path.c_str(), 0));
90 }
91
92 if (testOnly) {
93 this->setTestFontManager(std::move(fFontProvider));
94 } else {
95 this->setAssetFontManager(std::move(fFontProvider));
96 }
97 this->disableFontFallback();
98 }
99
100 size_t resolvedFonts() const { return fResolvedFonts; }
101
102 // TODO: temp solution until we check in fonts
103 bool fontsFound() const { return fFontsFound; }
104
105private:
106 bool fFontsFound;
107 size_t fResolvedFonts;
108 std::string fResourceDir;
109 sk_sp<TypefaceFontProvider> fFontProvider;
110};
111
112// buffer must be at least MAX_TEXT_LENGTH in length.
113// Returns size of text placed in buffer.
114template <typename T>
115uint8_t RandomText(T* buffer, Fuzz* fuzz) {
116 uint8_t text_length;
117 fuzz->nextRange(&text_length, 0, MAX_TEXT_LENGTH);
118 fuzz->nextN(buffer, text_length);
119 return text_length;
120}
121
122// Add random bytes to the paragraph.
123void AddASCIIText(ParagraphBuilder* builder,Fuzz* fuzz) {
124 char text[MAX_TEXT_LENGTH];
125 const auto text_length = RandomText(text, fuzz);
126 builder->addText(text, text_length);
127}
128// Add random bytes to the paragraph.
129void AddUnicodeText(ParagraphBuilder* builder,Fuzz* fuzz) {
130 char16_t text[MAX_TEXT_LENGTH];
131 const auto text_length = RandomText(text, fuzz);
132 builder->addText(std::u16string(text, text_length));
133}
134
135// Combining characters to produce 'Zalgo' text.
136const std::u16string COMBINING_DOWN = u"\u0316\u0317\u0318\u0319\u031c\u031d\u031e\u031f\u0320\u0324\u0325\u0326\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0339\u033a\u033b\u033c\u0345\u0347\u0348\u0349\u034d\u034e\u0353\u0354\u0355\u0356\u0359\u035a\u0323";
137const std::u16string COMBINING_UP = u"\u030d\u030e\u0304\u0305\u033f\u0311\u0306\u0310\u0352\u0357\u0351\u0307\u0308\u030a\u0342\u0343\u0344\u034a\u034b\u034c\u0303\u0302\u030c\u0350\u0300\u0301\u030b\u030f\u0312\u0313\u0314\u033d\u0309\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u035b\u0346\u031a";
138const std::u16string COMBINING_MIDDLE = u"\u0315\u031b\u0340\u0341\u0358\u0321\u0322\u0327\u0328\u0334\u0335\u0336\u034f\u035c\u035d\u035e\u035f\u0360\u0362\u0338\u0337\u0361\u0489";
139// Add random Zalgo text to the paragraph.
140void AddZalgoText(ParagraphBuilder* builder, Fuzz* fuzz) {
141 char text[MAX_TEXT_LENGTH];
142 const auto text_length = RandomText(text, fuzz);
143 std::u16string result;
144
145 for (auto& c : std::string(text, text_length)) {
146 result += c;
147 uint8_t mark_count;
148 fuzz->next(&mark_count);
149 for (int i = 0; i < mark_count; i++) {
150 uint8_t mark_type, mark_index;
151 fuzz->next(&mark_type, &mark_index);
152 switch (mark_type % 3) {
153 case 0:
154 result += COMBINING_UP[mark_index % COMBINING_UP.size()];
155 break;
156 case 1:
157 result += COMBINING_MIDDLE[mark_index % COMBINING_MIDDLE.size()];
158 break;
159 case 2:
160 default:
161 result += COMBINING_DOWN[mark_index % COMBINING_DOWN.size()];
162 break;
163 }
164 }
165 }
166 builder->addText(result);
167}
168
169void AddStyle(ParagraphBuilder* builder, Fuzz* fuzz) {
170 // TODO(westont): Make this probabilistic, and fill in the remaining TextStyle fields.
171 TextStyle ts;
172 ts.setFontFamilies({SkString("Roboto")});
173 //ts.setColor(SK_ColorBLACK);
174 //ts.setForegroundColor
175 //ts.setBackgroundColor
176 //ts.setDecoration(TextDecoration decoration);
177 //ts.setDecorationMode(TextDecorationMode mode);
178 //ts.setDecorationStyle(TextDecorationStyle style);
179 //ts.setDecorationColor(SkColor color);
180 //ts.setDecorationThicknessMultiplier(SkScalar m);
181 //ts.setFontStyle
182 //ts.addShadow
183 //ts.addFontFeature
184 //ts.setFontSize
185 //ts.setHeight
186 //ts.setHeightOverride
187 //ts.setletterSpacing
188 //ts.setWordSpacing
189 //ts.setTypeface
190 //ts.setLocale
191 //ts.setTextBaseline
192 //ts.setPlaceholder
193
194 builder->pushStyle(ts);
195}
196void RemoveStyle(ParagraphBuilder* builder, Fuzz* fuzz) {
197 bool pop;
198 fuzz->next(&pop);
199 if (pop) {
200 builder->pop();
201 }
202}
203
204void AddStyleAndText(ParagraphBuilder* builder, Fuzz* fuzz) {
205 AddStyle(builder, fuzz);
206 uint8_t text_type;
207 fuzz->next(&text_type);
208 switch (text_type % 3) {
209 case 0:
210 AddASCIIText(builder, fuzz);
211 break;
212 case 1:
213 AddUnicodeText(builder, fuzz);
214 break;
215 case 2:
216 AddZalgoText(builder, fuzz);
217 break;
218 }
219 RemoveStyle(builder, fuzz);
220
221}
222
223ParagraphStyle BuildParagraphStyle(Fuzz* fuzz) {
225 bool hinting;
226 fuzz->next(&hinting);
227 if (hinting) {
228 ps.turnHintingOff();
229 }
230 StrutStyle ss;
231 // TODO(westont): Fuzz this object.
232 ps.setStrutStyle(ss);
233 TextDirection td;
234 fuzz->nextEnum(&td, TextDirection::kRtl);
235 ps.setTextDirection(td);
236 TextAlign ta;
237 fuzz->nextEnum(&ta, TextAlign::kEnd);
238 ps.setTextAlign(ta);
239 size_t ml;
240 fuzz->next(&ml);
241 ps.setMaxLines(ml);
242 // TODO(westont): Randomize with other values and no value.
243 ps.setEllipsis(u"\u2026");
244 SkScalar h;
245 fuzz->next(&h);
246 ps.setHeight(h);
247 TextHeightBehavior thb = TextHeightBehavior::kAll;
248 // TODO(westont): This crashes our seed test case, why?
249 //fuzz->nextEnum(&thb, TextHeightBehavior::kDisableAll);
250 ps.setTextHeightBehavior(thb);
251
252 return ps;
253}
254
256 auto factory = SkShapers::BestAvailable();
257 return sk_ref_sp<SkUnicode>(factory->getUnicode());
258}
259
260} // namespace
261
262DEF_FUZZ(SkParagraph, fuzz) {
263 static sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
264 ParagraphStyle paragraph_style = BuildParagraphStyle(fuzz);
265 ParagraphBuilderImpl builder(paragraph_style, fontCollection, get_unicode());
266
267 uint8_t iterations;
268 fuzz->nextRange(&iterations, 1, MAX_TEXT_ADDITIONS);
269 for (int i = 0; i < iterations; i++) {
270 AddStyleAndText(&builder, fuzz);
271 }
272 // TODO(westont): Figure out if we can get more converage by having fontsFound, current
273 // they're not.
274 // if (!fontCollection->fontsFound()) return;
275
276 builder.pop();
277 auto paragraph = builder.Build();
278
279 SkBitmap bm;
280 if (!bm.tryAllocN32Pixels(TEST_CANVAS_DIM, TEST_CANVAS_DIM)) {
281 return;
282 }
283 SkCanvas canvas(bm);
284 uint8_t layout_width;
285 fuzz->next(&layout_width);
286 paragraph->layout(layout_width);
287 paragraph->paint(&canvas, 0, 0);
288}
289
290#endif // SK_ENABLE_PARAGRAPH
#define DEF_FUZZ(name, f)
Definition: Fuzz.h:156
SkString GetResourcePath(const char *resource)
Definition: Resources.cpp:23
static std::function< void(void)> pop(std::deque< std::function< void(void)> > *list)
Definition: SkExecutor.cpp:62
sk_sp< T > sk_make_sp(Args &&... args)
Definition: SkRefCnt.h:371
sk_sp< SkUnicode > get_unicode()
Definition: Fuzz.h:24
void next(T *t)
Definition: Fuzz.h:64
void nextRange(T *, Min, Max)
Definition: Fuzz.h:119
void nextEnum(T *ptr, T max)
Definition: Fuzz.h:131
void nextN(T *ptr, int n)
Definition: Fuzz.h:144
bool tryAllocN32Pixels(int width, int height, bool isOpaque=false)
Definition: SkBitmap.cpp:226
sk_sp< SkTypeface > makeFromFile(const char path[], int ttcIndex=0) const
Definition: SkFontMgr.cpp:143
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:534
const char * c_str() const
Definition: SkString.h:133
void setFontFamilies(std::vector< SkString > families)
Definition: TextStyle.h:253
float SkScalar
Definition: extension.cpp:12
GAsyncResult * result
std::u16string text
sk_sp< Factory > BestAvailable()
sk_sp< SkFontMgr > TestFontMgr()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset fonts
Definition: switches.h:213
font
Font Metadata and Metrics.
SkScalar h
#define T
Definition: precompiler.cc:65
void setEllipsis(const std::u16string &ellipsis)
void setTextDirection(TextDirection direction)
void setMaxLines(size_t maxLines)
void setHeight(SkScalar height)
void setStrutStyle(StrutStyle strutStyle)
void setTextHeightBehavior(TextHeightBehavior v)
void setTextAlign(TextAlign align)
int32_t mark_count