[fuzzing] Add first pass SkParagraph fuzzing.
Rough first pass at SkParagraph fuzzing. Lots of things not yet fuzzed. --FontCollection cribbed from SkParagraphTest --Current flow: ---Fuzz ParagraphStyle ---Add text and style some random small number of times. ---Text is either ASCII, unicode, or 'Zalgo'. Although there are many todos, want to go ahead and submit this ~unchanged so the existing test cases that have found bugs are not invalidated by a changing binary. Change-Id: I38adca5fa79cfb20068fdf2fb431f90de55a2afc Bug: skia:10894 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/336438 Commit-Queue: Weston Tracey <westont@google.com> Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
parent
72bd9e8664
commit
6d4577bc52
2
BUILD.gn
2
BUILD.gn
@ -2230,6 +2230,7 @@ if (skia_enable_tools) {
|
||||
"fuzz/FuzzPathop.cpp",
|
||||
"fuzz/FuzzPolyUtils.cpp",
|
||||
"fuzz/FuzzRegionOp.cpp",
|
||||
"fuzz/FuzzSkParagraph.cpp",
|
||||
"fuzz/oss_fuzz/FuzzAndroidCodec.cpp",
|
||||
"fuzz/oss_fuzz/FuzzAnimatedImage.cpp",
|
||||
"fuzz/oss_fuzz/FuzzImage.cpp",
|
||||
@ -2259,6 +2260,7 @@ if (skia_enable_tools) {
|
||||
":gpu_tool_utils",
|
||||
":skia",
|
||||
"modules/skottie:fuzz",
|
||||
"modules/skparagraph",
|
||||
]
|
||||
}
|
||||
|
||||
|
274
fuzz/FuzzSkParagraph.cpp
Normal file
274
fuzz/FuzzSkParagraph.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "fuzz/Fuzz.h"
|
||||
#include "fuzz/FuzzCommon.h"
|
||||
#include "include/core/SkBitmap.h"
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkEncodedImageFormat.h"
|
||||
#include "include/core/SkFontMgr.h"
|
||||
#include "include/core/SkFontStyle.h"
|
||||
#include "include/core/SkImageEncoder.h"
|
||||
#include "include/core/SkPaint.h"
|
||||
#include "include/core/SkPoint.h"
|
||||
#include "include/core/SkRect.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/core/SkScalar.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/core/SkString.h"
|
||||
#include "include/core/SkTypeface.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "modules/skparagraph/include/DartTypes.h"
|
||||
#include "modules/skparagraph/include/FontCollection.h"
|
||||
#include "modules/skparagraph/include/Paragraph.h"
|
||||
#include "modules/skparagraph/include/ParagraphCache.h"
|
||||
#include "modules/skparagraph/include/ParagraphStyle.h"
|
||||
#include "modules/skparagraph/include/TextShadow.h"
|
||||
#include "modules/skparagraph/include/TextStyle.h"
|
||||
#include "modules/skparagraph/include/TypefaceFontProvider.h"
|
||||
#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
|
||||
#include "modules/skparagraph/src/ParagraphImpl.h"
|
||||
#include "modules/skparagraph/src/Run.h"
|
||||
#include "modules/skparagraph/src/TextLine.h"
|
||||
#include "modules/skparagraph/utils/TestFontCollection.h"
|
||||
#include "src/core/SkOSFile.h"
|
||||
#include "src/core/SkSpan.h"
|
||||
#include "src/utils/SkOSPath.h"
|
||||
#include "src/utils/SkShaperJSONWriter.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/Resources.h"
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace skia::textlayout;
|
||||
namespace {
|
||||
const uint8_t MAX_TEXT_LENGTH = 255;
|
||||
const uint8_t MAX_TEXT_ADDITIONS = 4;
|
||||
const uint16_t TEST_CANVAS_WIDTH = 1000;
|
||||
|
||||
class ResourceFontCollection : public FontCollection {
|
||||
public:
|
||||
ResourceFontCollection(bool testOnly = false)
|
||||
: fFontsFound(false)
|
||||
, fResolvedFonts(0)
|
||||
, fResourceDir(GetResourcePath("fonts").c_str())
|
||||
, fFontProvider(sk_make_sp<TypefaceFontProvider>()) {
|
||||
std::vector<SkString> fonts;
|
||||
SkOSFile::Iter iter(fResourceDir.c_str());
|
||||
|
||||
SkString path;
|
||||
while (iter.next(&path)) {
|
||||
if (path.endsWith("Roboto-Italic.ttf")) {
|
||||
fFontsFound = true;
|
||||
}
|
||||
fonts.emplace_back(path);
|
||||
}
|
||||
|
||||
if (!fFontsFound) {
|
||||
// SkDebugf("Fonts not found, skipping all the tests\n");
|
||||
return;
|
||||
}
|
||||
// Only register fonts if we have to
|
||||
for (auto& font : fonts) {
|
||||
SkString file_path;
|
||||
file_path.printf("%s/%s", fResourceDir.c_str(), font.c_str());
|
||||
fFontProvider->registerTypeface(SkTypeface::MakeFromFile(file_path.c_str()));
|
||||
}
|
||||
|
||||
if (testOnly) {
|
||||
this->setTestFontManager(std::move(fFontProvider));
|
||||
} else {
|
||||
this->setAssetFontManager(std::move(fFontProvider));
|
||||
}
|
||||
this->disableFontFallback();
|
||||
}
|
||||
|
||||
size_t resolvedFonts() const { return fResolvedFonts; }
|
||||
|
||||
// TODO: temp solution until we check in fonts
|
||||
bool fontsFound() const { return fFontsFound; }
|
||||
|
||||
private:
|
||||
bool fFontsFound;
|
||||
size_t fResolvedFonts;
|
||||
std::string fResourceDir;
|
||||
sk_sp<TypefaceFontProvider> fFontProvider;
|
||||
};
|
||||
|
||||
// buffer must be at least MAX_TEXT_LENGTH in length.
|
||||
// Returns size of text placed in buffer.
|
||||
template <typename T>
|
||||
uint8_t RandomText(T* buffer, Fuzz* fuzz) {
|
||||
uint8_t text_length;
|
||||
fuzz->nextRange(&text_length, 0, MAX_TEXT_LENGTH);
|
||||
fuzz->nextN(buffer, text_length);
|
||||
return text_length;
|
||||
}
|
||||
|
||||
// Add random bytes to the paragraph.
|
||||
void AddASCIIText(ParagraphBuilder* builder,Fuzz* fuzz) {
|
||||
char text[MAX_TEXT_LENGTH];
|
||||
const auto text_length = RandomText(text, fuzz);
|
||||
builder->addText(text, text_length);
|
||||
}
|
||||
// Add random bytes to the paragraph.
|
||||
void AddUnicodeText(ParagraphBuilder* builder,Fuzz* fuzz) {
|
||||
char16_t text[MAX_TEXT_LENGTH];
|
||||
const auto text_length = RandomText(text, fuzz);
|
||||
builder->addText(std::u16string(text, text_length));
|
||||
}
|
||||
|
||||
// Combining characters to produce 'Zalgo' text.
|
||||
const 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";
|
||||
const 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";
|
||||
const 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";
|
||||
// Add random Zalgo text to the paragraph.
|
||||
void AddZalgoText(ParagraphBuilder* builder, Fuzz* fuzz) {
|
||||
char text[MAX_TEXT_LENGTH];
|
||||
const auto text_length = RandomText(text, fuzz);
|
||||
std::u16string result;
|
||||
|
||||
for (auto& c : std::string(text, text_length)) {
|
||||
result += c;
|
||||
uint8_t mark_count;
|
||||
fuzz->next(&mark_count);
|
||||
for (int i = 0; i < mark_count; i++) {
|
||||
uint8_t mark_type, mark_index;
|
||||
fuzz->next(&mark_type, &mark_index);
|
||||
switch (mark_type % 3) {
|
||||
case 0:
|
||||
result += COMBINING_UP[mark_index % COMBINING_UP.size()];
|
||||
break;
|
||||
case 1:
|
||||
result += COMBINING_MIDDLE[mark_index % COMBINING_MIDDLE.size()];
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
result += COMBINING_DOWN[mark_index % COMBINING_DOWN.size()];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
builder->addText(result);
|
||||
}
|
||||
|
||||
void AddStyle(ParagraphBuilder* builder, Fuzz* fuzz) {
|
||||
// TODO(westont): Make this probabilistic, and fill in the remaining TextStyle fields.
|
||||
TextStyle ts;
|
||||
ts.setFontFamilies({SkString("Roboto")});
|
||||
//ts.setColor(SK_ColorBLACK);
|
||||
//ts.setForegroundColor
|
||||
//ts.setBackgroundColor
|
||||
//ts.setDecoration(TextDecoration decoration);
|
||||
//ts.setDecorationMode(TextDecorationMode mode);
|
||||
//ts.setDecorationStyle(TextDecorationStyle style);
|
||||
//ts.setDecorationColor(SkColor color);
|
||||
//ts.setDecorationThicknessMultiplier(SkScalar m);
|
||||
//ts.setFontStyle
|
||||
//ts.addShadow
|
||||
//ts.addFontFeature
|
||||
//ts.setFontSize
|
||||
//ts.setHeight
|
||||
//ts.setHeightOverride
|
||||
//ts.setletterSpacing
|
||||
//ts.setWordSpacing
|
||||
//ts.setTypeface
|
||||
//ts.setLocale
|
||||
//ts.setTextBaseline
|
||||
//ts.setPlaceholder
|
||||
|
||||
builder->pushStyle(ts);
|
||||
}
|
||||
void RemoveStyle(ParagraphBuilder* builder, Fuzz* fuzz) {
|
||||
bool pop;
|
||||
fuzz->next(&pop);
|
||||
if (pop) {
|
||||
builder->pop();
|
||||
}
|
||||
}
|
||||
|
||||
void AddStyleAndText(ParagraphBuilder* builder, Fuzz* fuzz) {
|
||||
AddStyle(builder, fuzz);
|
||||
uint8_t text_type;
|
||||
fuzz->next(&text_type);
|
||||
switch (text_type % 3) {
|
||||
case 0:
|
||||
AddASCIIText(builder, fuzz);
|
||||
break;
|
||||
case 1:
|
||||
AddUnicodeText(builder, fuzz);
|
||||
break;
|
||||
case 2:
|
||||
AddZalgoText(builder, fuzz);
|
||||
break;
|
||||
}
|
||||
RemoveStyle(builder, fuzz);
|
||||
|
||||
}
|
||||
|
||||
ParagraphStyle BuildParagraphStyle(Fuzz* fuzz) {
|
||||
ParagraphStyle ps;
|
||||
bool hinting;
|
||||
fuzz->next(&hinting);
|
||||
if (hinting) {
|
||||
ps.turnHintingOff();
|
||||
}
|
||||
StrutStyle ss;
|
||||
// TODO(westont): Fuzz this object.
|
||||
ps.setStrutStyle(ss);
|
||||
TextDirection td;
|
||||
fuzz->nextEnum(&td, TextDirection::kRtl);
|
||||
ps.setTextDirection(td);
|
||||
TextAlign ta;
|
||||
fuzz->nextEnum(&ta, TextAlign::kEnd);
|
||||
ps.setTextAlign(ta);
|
||||
size_t ml;
|
||||
fuzz->next(&ml);
|
||||
ps.setMaxLines(ml);
|
||||
// TODO(westont): Randomize with other values and no value.
|
||||
ps.setEllipsis(u"\u2026");
|
||||
SkScalar h;
|
||||
fuzz->next(&h);
|
||||
ps.setHeight(h);
|
||||
TextHeightBehavior thb = TextHeightBehavior::kAll;
|
||||
// TODO(westont): This crashes our seed test case, why?
|
||||
//fuzz->nextEnum(&thb, TextHeightBehavior::kDisableAll);
|
||||
ps.setTextHeightBehavior(thb);
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEF_FUZZ(api_skparagraph, fuzz) {
|
||||
static sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
|
||||
ParagraphStyle paragraph_style = BuildParagraphStyle(fuzz);
|
||||
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
|
||||
|
||||
uint8_t iterations;
|
||||
fuzz->nextRange(&iterations, 1, MAX_TEXT_ADDITIONS);
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
AddStyleAndText(&builder, fuzz);
|
||||
}
|
||||
// TODO(westont): Figure out if we can get more converage by having fontsFound, current
|
||||
// they're not.
|
||||
// if (!fontCollection->fontsFound()) return;
|
||||
|
||||
builder.pop();
|
||||
auto paragraph = builder.Build();
|
||||
|
||||
paragraph->layout(TEST_CANVAS_WIDTH);
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user