mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
559 lines
18 KiB
C++
559 lines
18 KiB
C++
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "flutter/testing/mock_canvas.h"
|
|
|
|
#include "flutter/fml/logging.h"
|
|
#include "flutter/testing/display_list_testing.h"
|
|
#include "third_party/skia/include/core/SkImageInfo.h"
|
|
#include "third_party/skia/include/core/SkSerialProcs.h"
|
|
#include "third_party/skia/include/core/SkSize.h"
|
|
#include "third_party/skia/include/core/SkTextBlob.h"
|
|
|
|
namespace flutter {
|
|
namespace testing {
|
|
|
|
constexpr SkISize kSize = SkISize::Make(64, 64);
|
|
|
|
MockCanvas::MockCanvas()
|
|
: tracker_(SkRect::Make(kSize), SkMatrix::I()), current_layer_(0) {}
|
|
|
|
MockCanvas::MockCanvas(int width, int height)
|
|
: tracker_(SkRect::MakeIWH(width, height), SkMatrix::I()),
|
|
current_layer_(0) {}
|
|
|
|
MockCanvas::~MockCanvas() {
|
|
EXPECT_EQ(current_layer_, 0);
|
|
}
|
|
|
|
SkISize MockCanvas::GetBaseLayerSize() const {
|
|
return tracker_.base_device_cull_rect().roundOut().size();
|
|
}
|
|
|
|
SkImageInfo MockCanvas::GetImageInfo() const {
|
|
SkISize size = GetBaseLayerSize();
|
|
return SkImageInfo::MakeUnknown(size.width(), size.height());
|
|
}
|
|
|
|
void MockCanvas::Save() {
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_, SaveData{current_layer_ + 1}});
|
|
tracker_.save();
|
|
current_layer_++; // Must go here; func params order of eval is undefined
|
|
}
|
|
|
|
void MockCanvas::SaveLayer(const SkRect* bounds,
|
|
const DlPaint* paint,
|
|
const DlImageFilter* backdrop) {
|
|
// saveLayer calls this prior to running, so we use it to track saveLayer
|
|
// calls
|
|
draw_calls_.emplace_back(DrawCall{
|
|
current_layer_,
|
|
SaveLayerData{bounds ? *bounds : SkRect(), paint ? *paint : DlPaint(),
|
|
backdrop ? backdrop->shared() : nullptr,
|
|
current_layer_ + 1}});
|
|
tracker_.save();
|
|
current_layer_++; // Must go here; func params order of eval is undefined
|
|
}
|
|
|
|
void MockCanvas::Restore() {
|
|
FML_DCHECK(current_layer_ > 0);
|
|
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_, RestoreData{current_layer_ - 1}});
|
|
tracker_.restore();
|
|
current_layer_--; // Must go here; func params order of eval is undefined
|
|
}
|
|
|
|
// clang-format off
|
|
|
|
// 2x3 2D affine subset of a 4x4 transform in row major order
|
|
void MockCanvas::Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt,
|
|
SkScalar myx, SkScalar myy, SkScalar myt) {
|
|
Transform(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, 0, 0, 1));
|
|
}
|
|
|
|
// full 4x4 transform in row major order
|
|
void MockCanvas::TransformFullPerspective(
|
|
SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
|
|
SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
|
|
SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
|
|
SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) {
|
|
Transform(SkM44(mxx, mxy, mxz, mxt,
|
|
myx, myy, myz, myt,
|
|
mzx, mzy, mzz, mzt,
|
|
mwx, mwy, mwz, mwt));
|
|
}
|
|
|
|
// clang-format on
|
|
|
|
void MockCanvas::Transform(const SkMatrix* matrix) {
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_, ConcatMatrixData{SkM44(*matrix)}});
|
|
tracker_.transform(*matrix);
|
|
}
|
|
|
|
void MockCanvas::Transform(const SkM44* matrix) {
|
|
draw_calls_.emplace_back(DrawCall{current_layer_, ConcatMatrixData{*matrix}});
|
|
tracker_.transform(*matrix);
|
|
}
|
|
|
|
void MockCanvas::SetTransform(const SkMatrix* matrix) {
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_, SetMatrixData{SkM44(*matrix)}});
|
|
tracker_.setTransform(*matrix);
|
|
}
|
|
|
|
void MockCanvas::SetTransform(const SkM44* matrix) {
|
|
draw_calls_.emplace_back(DrawCall{current_layer_, SetMatrixData{*matrix}});
|
|
tracker_.setTransform(*matrix);
|
|
}
|
|
|
|
void MockCanvas::TransformReset() {
|
|
draw_calls_.emplace_back(DrawCall{current_layer_, SetMatrixData{SkM44()}});
|
|
tracker_.setIdentity();
|
|
}
|
|
|
|
void MockCanvas::Translate(SkScalar x, SkScalar y) {
|
|
this->Transform(SkM44::Translate(x, y));
|
|
}
|
|
|
|
void MockCanvas::Scale(SkScalar x, SkScalar y) {
|
|
this->Transform(SkM44::Scale(x, y));
|
|
}
|
|
|
|
void MockCanvas::Rotate(SkScalar degrees) {
|
|
this->Transform(SkMatrix::RotateDeg(degrees));
|
|
}
|
|
|
|
void MockCanvas::Skew(SkScalar sx, SkScalar sy) {
|
|
this->Transform(SkMatrix::Skew(sx, sy));
|
|
}
|
|
|
|
SkM44 MockCanvas::GetTransformFullPerspective() const {
|
|
return tracker_.matrix_4x4();
|
|
}
|
|
|
|
SkMatrix MockCanvas::GetTransform() const {
|
|
return tracker_.matrix_3x3();
|
|
}
|
|
|
|
void MockCanvas::DrawTextBlob(const sk_sp<SkTextBlob>& text,
|
|
SkScalar x,
|
|
SkScalar y,
|
|
const DlPaint& paint) {
|
|
// This duplicates existing logic in SkCanvas::onDrawPicture
|
|
// that should probably be split out so it doesn't need to be here as well.
|
|
// SkRect storage;
|
|
// if (paint.canComputeFastBounds()) {
|
|
// storage = text->bounds().makeOffset(x, y);
|
|
// SkRect tmp;
|
|
// if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
|
|
// return;
|
|
// }
|
|
// }
|
|
|
|
draw_calls_.emplace_back(DrawCall{
|
|
current_layer_, DrawTextData{text ? text->serialize(SkSerialProcs{})
|
|
: SkData::MakeUninitialized(0),
|
|
paint, SkPoint::Make(x, y)}});
|
|
}
|
|
|
|
void MockCanvas::DrawTextFrame(
|
|
const std::shared_ptr<impeller::TextFrame>& text_frame,
|
|
SkScalar x,
|
|
SkScalar y,
|
|
const DlPaint& paint) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawRect(const SkRect& rect, const DlPaint& paint) {
|
|
draw_calls_.emplace_back(DrawCall{current_layer_, DrawRectData{rect, paint}});
|
|
}
|
|
|
|
void MockCanvas::DrawPath(const SkPath& path, const DlPaint& paint) {
|
|
draw_calls_.emplace_back(DrawCall{current_layer_, DrawPathData{path, paint}});
|
|
}
|
|
|
|
void MockCanvas::DrawShadow(const SkPath& path,
|
|
const DlColor color,
|
|
const SkScalar elevation,
|
|
bool transparent_occluder,
|
|
SkScalar dpr) {
|
|
draw_calls_.emplace_back(DrawCall{
|
|
current_layer_,
|
|
DrawShadowData{path, color, elevation, transparent_occluder, dpr}});
|
|
}
|
|
|
|
void MockCanvas::DrawImage(const sk_sp<DlImage>& image,
|
|
SkPoint point,
|
|
const DlImageSampling options,
|
|
const DlPaint* paint) {
|
|
if (paint) {
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_,
|
|
DrawImageData{image, point.fX, point.fY, options, *paint}});
|
|
} else {
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_,
|
|
DrawImageDataNoPaint{image, point.fX, point.fY, options}});
|
|
}
|
|
}
|
|
|
|
void MockCanvas::DrawDisplayList(const sk_sp<DisplayList> display_list,
|
|
SkScalar opacity) {
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_, DrawDisplayListData{display_list, opacity}});
|
|
}
|
|
|
|
void MockCanvas::ClipRect(const SkRect& rect, ClipOp op, bool is_aa) {
|
|
ClipEdgeStyle style = is_aa ? kSoftClipEdgeStyle : kHardClipEdgeStyle;
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_, ClipRectData{rect, op, style}});
|
|
tracker_.clipRect(rect, op, is_aa);
|
|
}
|
|
|
|
void MockCanvas::ClipRRect(const SkRRect& rrect, ClipOp op, bool is_aa) {
|
|
ClipEdgeStyle style = is_aa ? kSoftClipEdgeStyle : kHardClipEdgeStyle;
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_, ClipRRectData{rrect, op, style}});
|
|
tracker_.clipRRect(rrect, op, is_aa);
|
|
}
|
|
|
|
void MockCanvas::ClipPath(const SkPath& path, ClipOp op, bool is_aa) {
|
|
ClipEdgeStyle style = is_aa ? kSoftClipEdgeStyle : kHardClipEdgeStyle;
|
|
draw_calls_.emplace_back(
|
|
DrawCall{current_layer_, ClipPathData{path, op, style}});
|
|
tracker_.clipPath(path, op, is_aa);
|
|
}
|
|
|
|
SkRect MockCanvas::GetDestinationClipBounds() const {
|
|
return tracker_.device_cull_rect();
|
|
}
|
|
|
|
SkRect MockCanvas::GetLocalClipBounds() const {
|
|
return tracker_.local_cull_rect();
|
|
}
|
|
|
|
bool MockCanvas::QuickReject(const SkRect& bounds) const {
|
|
return tracker_.content_culled(bounds);
|
|
}
|
|
|
|
void MockCanvas::DrawDRRect(const SkRRect&, const SkRRect&, const DlPaint&) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawPaint(const DlPaint& paint) {
|
|
draw_calls_.emplace_back(DrawCall{current_layer_, DrawPaintData{paint}});
|
|
}
|
|
|
|
void MockCanvas::DrawColor(DlColor color, DlBlendMode mode) {
|
|
DrawPaint(DlPaint(color).setBlendMode(mode));
|
|
}
|
|
|
|
void MockCanvas::DrawLine(const SkPoint& p0,
|
|
const SkPoint& p1,
|
|
const DlPaint& paint) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawPoints(PointMode,
|
|
uint32_t,
|
|
const SkPoint[],
|
|
const DlPaint&) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawOval(const SkRect&, const DlPaint&) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawCircle(const SkPoint& center,
|
|
SkScalar radius,
|
|
const DlPaint& paint) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawArc(const SkRect&,
|
|
SkScalar,
|
|
SkScalar,
|
|
bool,
|
|
const DlPaint&) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawRRect(const SkRRect&, const DlPaint&) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawImageRect(const sk_sp<DlImage>&,
|
|
const SkRect&,
|
|
const SkRect&,
|
|
const DlImageSampling,
|
|
const DlPaint*,
|
|
SrcRectConstraint constraint) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawImageNine(const sk_sp<DlImage>& image,
|
|
const SkIRect& center,
|
|
const SkRect& dst,
|
|
DlFilterMode filter,
|
|
const DlPaint* paint) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawVertices(const DlVertices*, DlBlendMode, const DlPaint&) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::DrawAtlas(const sk_sp<DlImage>&,
|
|
const SkRSXform[],
|
|
const SkRect[],
|
|
const DlColor[],
|
|
int,
|
|
DlBlendMode,
|
|
const DlImageSampling,
|
|
const SkRect*,
|
|
const DlPaint*) {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
void MockCanvas::Flush() {
|
|
FML_DCHECK(false);
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// A few ostream operators duplicated from assertions_skia.cc
|
|
// In the short term, there are issues trying to include that file
|
|
// here because it appears in a skia-targeted testing source set
|
|
// and in the long term, DlCanvas, and therefore this file will
|
|
// eventually be cleaned of these SkObject dependencies and these
|
|
// ostream operators will be converted to their DL equivalents.
|
|
static std::ostream& operator<<(std::ostream& os, const SkPoint& r) {
|
|
return os << "XY: " << r.fX << ", " << r.fY;
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& os, const SkRect& r) {
|
|
return os << "LTRB: " << r.fLeft << ", " << r.fTop << ", " << r.fRight << ", "
|
|
<< r.fBottom;
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& os, const SkRRect& r) {
|
|
return os << "LTRB: " << r.rect().fLeft << ", " << r.rect().fTop << ", "
|
|
<< r.rect().fRight << ", " << r.rect().fBottom;
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& os, const SkPath& r) {
|
|
return os << "Valid: " << r.isValid()
|
|
<< ", FillType: " << static_cast<int>(r.getFillType())
|
|
<< ", Bounds: " << r.getBounds();
|
|
}
|
|
// --------------------------------------------------------
|
|
|
|
static std::ostream& operator<<(std::ostream& os, const SkM44& m) {
|
|
os << m.rc(0, 0) << ", " << m.rc(0, 1) << ", " << m.rc(0, 2) << ", "
|
|
<< m.rc(0, 3) << std::endl;
|
|
os << m.rc(1, 0) << ", " << m.rc(1, 1) << ", " << m.rc(1, 2) << ", "
|
|
<< m.rc(1, 3) << std::endl;
|
|
os << m.rc(2, 0) << ", " << m.rc(2, 1) << ", " << m.rc(2, 2) << ", "
|
|
<< m.rc(2, 3) << std::endl;
|
|
os << m.rc(3, 0) << ", " << m.rc(3, 1) << ", " << m.rc(3, 2) << ", "
|
|
<< m.rc(3, 3);
|
|
return os;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::SaveData& a, const MockCanvas::SaveData& b) {
|
|
return a.save_to_layer == b.save_to_layer;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const MockCanvas::SaveData& data) {
|
|
return os << data.save_to_layer;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::SaveLayerData& a,
|
|
const MockCanvas::SaveLayerData& b) {
|
|
return a.save_bounds == b.save_bounds && a.restore_paint == b.restore_paint &&
|
|
Equals(a.backdrop_filter, b.backdrop_filter) &&
|
|
a.save_to_layer == b.save_to_layer;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::SaveLayerData& data) {
|
|
return os << data.save_bounds << " " << data.restore_paint << " "
|
|
<< data.backdrop_filter << " " << data.save_to_layer;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::RestoreData& a,
|
|
const MockCanvas::RestoreData& b) {
|
|
return a.restore_to_layer == b.restore_to_layer;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::RestoreData& data) {
|
|
return os << data.restore_to_layer;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::ConcatMatrixData& a,
|
|
const MockCanvas::ConcatMatrixData& b) {
|
|
return a.matrix == b.matrix;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::ConcatMatrixData& data) {
|
|
return os << data.matrix;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::SetMatrixData& a,
|
|
const MockCanvas::SetMatrixData& b) {
|
|
return a.matrix == b.matrix;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::SetMatrixData& data) {
|
|
return os << data.matrix;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawRectData& a,
|
|
const MockCanvas::DrawRectData& b) {
|
|
return a.rect == b.rect && a.paint == b.paint;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawRectData& data) {
|
|
return os << data.rect << " " << data.paint;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawPathData& a,
|
|
const MockCanvas::DrawPathData& b) {
|
|
return a.path == b.path && a.paint == b.paint;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawPathData& data) {
|
|
return os << data.path << " " << data.paint;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawTextData& a,
|
|
const MockCanvas::DrawTextData& b) {
|
|
return a.serialized_text->equals(b.serialized_text.get()) &&
|
|
a.paint == b.paint && a.offset == b.offset;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawTextData& data) {
|
|
return os << data.serialized_text << " " << data.paint << " " << data.offset;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawImageData& a,
|
|
const MockCanvas::DrawImageData& b) {
|
|
return a.image == b.image && a.x == b.x && a.y == b.y &&
|
|
a.options == b.options && a.paint == b.paint;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawImageData& data) {
|
|
return os << data.image << " " << data.x << " " << data.y << " "
|
|
<< data.options << " " << data.paint;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawImageDataNoPaint& a,
|
|
const MockCanvas::DrawImageDataNoPaint& b) {
|
|
return a.image == b.image && a.x == b.x && a.y == b.y &&
|
|
a.options == b.options;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawImageDataNoPaint& data) {
|
|
return os << data.image << " " << data.x << " " << data.y << " "
|
|
<< data.options;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawDisplayListData& a,
|
|
const MockCanvas::DrawDisplayListData& b) {
|
|
return a.display_list->Equals(b.display_list) && a.opacity == b.opacity;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawDisplayListData& data) {
|
|
auto dl = data.display_list;
|
|
return os << "[" << dl->unique_id() << " " << dl->op_count() << " "
|
|
<< dl->bytes() << "] " << data.opacity;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawShadowData& a,
|
|
const MockCanvas::DrawShadowData& b) {
|
|
return a.path == b.path && a.color == b.color && a.elevation == b.elevation &&
|
|
a.transparent_occluder == b.transparent_occluder && a.dpr == b.dpr;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawShadowData& data) {
|
|
return os << data.path << " " << data.color << " " << data.elevation << " "
|
|
<< data.transparent_occluder << " " << data.dpr;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::ClipRectData& a,
|
|
const MockCanvas::ClipRectData& b) {
|
|
return a.rect == b.rect && a.clip_op == b.clip_op && a.style == b.style;
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::ClipEdgeStyle& style) {
|
|
return os << (style == MockCanvas::kSoftClipEdgeStyle ? "kSoftEdges"
|
|
: "kHardEdges");
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::ClipRectData& data) {
|
|
return os << data.rect << " " << data.clip_op << " " << data.style;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::ClipRRectData& a,
|
|
const MockCanvas::ClipRRectData& b) {
|
|
return a.rrect == b.rrect && a.clip_op == b.clip_op && a.style == b.style;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::ClipRRectData& data) {
|
|
return os << data.rrect << " " << data.clip_op << " " << data.style;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::ClipPathData& a,
|
|
const MockCanvas::ClipPathData& b) {
|
|
return a.path == b.path && a.clip_op == b.clip_op && a.style == b.style;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::ClipPathData& data) {
|
|
return os << data.path << " " << data.clip_op << " " << data.style;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawCallData& data) {
|
|
std::visit([&os](auto& d) { os << d; }, data);
|
|
return os;
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawCall& a, const MockCanvas::DrawCall& b) {
|
|
return a.layer == b.layer && a.data == b.data;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const MockCanvas::DrawCall& draw) {
|
|
return os << "[Layer: " << draw.layer << ", Data: " << draw.data << "]";
|
|
}
|
|
|
|
bool operator==(const MockCanvas::DrawPaintData& a,
|
|
const MockCanvas::DrawPaintData& b) {
|
|
return a.paint == b.paint;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os,
|
|
const MockCanvas::DrawPaintData& data) {
|
|
return os << data.paint;
|
|
}
|
|
|
|
} // namespace testing
|
|
} // namespace flutter
|