1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2013 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "dm/DMJsonWriter.h"
9cb93a386Sopenharmony_ci#include "dm/DMSrcSink.h"
10cb93a386Sopenharmony_ci#include "gm/verifiers/gmverifier.h"
11cb93a386Sopenharmony_ci#include "include/codec/SkCodec.h"
12cb93a386Sopenharmony_ci#include "include/core/SkBBHFactory.h"
13cb93a386Sopenharmony_ci#include "include/core/SkColorPriv.h"
14cb93a386Sopenharmony_ci#include "include/core/SkColorSpace.h"
15cb93a386Sopenharmony_ci#include "include/core/SkData.h"
16cb93a386Sopenharmony_ci#include "include/core/SkDocument.h"
17cb93a386Sopenharmony_ci#include "include/core/SkGraphics.h"
18cb93a386Sopenharmony_ci#include "include/private/SkChecksum.h"
19cb93a386Sopenharmony_ci#include "include/private/SkHalf.h"
20cb93a386Sopenharmony_ci#include "include/private/SkSpinlock.h"
21cb93a386Sopenharmony_ci#include "include/private/SkTHash.h"
22cb93a386Sopenharmony_ci#include "src/core/SkColorSpacePriv.h"
23cb93a386Sopenharmony_ci#include "src/core/SkLeanWindows.h"
24cb93a386Sopenharmony_ci#include "src/core/SkMD5.h"
25cb93a386Sopenharmony_ci#include "src/core/SkOSFile.h"
26cb93a386Sopenharmony_ci#include "src/core/SkTaskGroup.h"
27cb93a386Sopenharmony_ci#include "src/utils/SkOSPath.h"
28cb93a386Sopenharmony_ci#include "tests/Test.h"
29cb93a386Sopenharmony_ci#include "tools/AutoreleasePool.h"
30cb93a386Sopenharmony_ci#include "tools/HashAndEncode.h"
31cb93a386Sopenharmony_ci#include "tools/ProcStats.h"
32cb93a386Sopenharmony_ci#include "tools/Resources.h"
33cb93a386Sopenharmony_ci#include "tools/ToolUtils.h"
34cb93a386Sopenharmony_ci#include "tools/flags/CommonFlags.h"
35cb93a386Sopenharmony_ci#include "tools/flags/CommonFlagsConfig.h"
36cb93a386Sopenharmony_ci#include "tools/ios_utils.h"
37cb93a386Sopenharmony_ci#include "tools/trace/ChromeTracingTracer.h"
38cb93a386Sopenharmony_ci#include "tools/trace/EventTracingPriv.h"
39cb93a386Sopenharmony_ci#include "tools/trace/SkDebugfTracer.h"
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci#include <memory>
42cb93a386Sopenharmony_ci#include <vector>
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci#include <stdlib.h>
45cb93a386Sopenharmony_ci
46cb93a386Sopenharmony_ci#ifndef SK_BUILD_FOR_WIN
47cb93a386Sopenharmony_ci    #include <unistd.h>
48cb93a386Sopenharmony_ci#endif
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) && defined(SK_HAS_HEIF_LIBRARY)
51cb93a386Sopenharmony_ci    #include <binder/IPCThreadState.h>
52cb93a386Sopenharmony_ci#endif
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_MAC)
55cb93a386Sopenharmony_ci    #include "include/utils/mac/SkCGUtils.h"
56cb93a386Sopenharmony_ci    #include "src/utils/mac/SkUniqueCFRef.h"
57cb93a386Sopenharmony_ci#endif
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_ciextern bool gSkForceRasterPipelineBlitter;
60cb93a386Sopenharmony_ciextern bool gUseSkVMBlitter;
61cb93a386Sopenharmony_ciextern bool gSkVMAllowJIT;
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_cistatic DEFINE_string(src, "tests gm skp mskp lottie rive svg image colorImage",
64cb93a386Sopenharmony_ci                     "Source types to test.");
65cb93a386Sopenharmony_cistatic DEFINE_bool(nameByHash, false,
66cb93a386Sopenharmony_ci                   "If true, write to FLAGS_writePath[0]/<hash>.png instead of "
67cb93a386Sopenharmony_ci                   "to FLAGS_writePath[0]/<config>/<sourceType>/<sourceOptions>/<name>.png");
68cb93a386Sopenharmony_cistatic DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests.");
69cb93a386Sopenharmony_cistatic DEFINE_string(matrix, "1 0 0 1",
70cb93a386Sopenharmony_ci                    "2x2 scale+skew matrix to apply or upright when using "
71cb93a386Sopenharmony_ci                    "'matrix' or 'upright' in config.");
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_cistatic DEFINE_string(skip, "",
74cb93a386Sopenharmony_ci        "Space-separated config/src/srcOptions/name quadruples to skip. "
75cb93a386Sopenharmony_ci        "'_' matches anything. '~' negates the match. E.g. \n"
76cb93a386Sopenharmony_ci        "'--skip gpu skp _ _' will skip all SKPs drawn into the gpu config.\n"
77cb93a386Sopenharmony_ci        "'--skip gpu skp _ _ 8888 gm _ aarects' will also skip the aarects GM on 8888.\n"
78cb93a386Sopenharmony_ci        "'--skip ~8888 svg _ svgparse_' blocks non-8888 SVGs that contain \"svgparse_\" in "
79cb93a386Sopenharmony_ci                                            "the name.");
80cb93a386Sopenharmony_ci
81cb93a386Sopenharmony_cistatic DEFINE_string2(readPath, r, "",
82cb93a386Sopenharmony_ci                      "If set check for equality with golden results in this directory.");
83cb93a386Sopenharmony_ciDEFINE_string2(writePath, w, "", "If set, write bitmaps here as .pngs.");
84cb93a386Sopenharmony_ci
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_cistatic DEFINE_string(uninterestingHashesFile, "",
87cb93a386Sopenharmony_ci        "File containing a list of uninteresting hashes. If a result hashes to something in "
88cb93a386Sopenharmony_ci        "this list, no image is written for that result.");
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_cistatic DEFINE_int(shards, 1, "We're splitting source data into this many shards.");
91cb93a386Sopenharmony_cistatic DEFINE_int(shard,  0, "Which shard do I run?");
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_cistatic DEFINE_string(mskps, "", "Directory to read mskps from, or a single mskp file.");
94cb93a386Sopenharmony_cistatic DEFINE_bool(forceRasterPipeline, false, "sets gSkForceRasterPipelineBlitter");
95cb93a386Sopenharmony_cistatic DEFINE_bool(skvm, false, "sets gUseSkVMBlitter");
96cb93a386Sopenharmony_cistatic DEFINE_bool(jit,  true,  "sets gSkVMAllowJIT");
97cb93a386Sopenharmony_ci
98cb93a386Sopenharmony_cistatic DEFINE_string(bisect, "",
99cb93a386Sopenharmony_ci        "Pair of: SKP file to bisect, followed by an l/r bisect trail string (e.g., 'lrll'). The "
100cb93a386Sopenharmony_ci        "l/r trail specifies which half to keep at each step of a binary search through the SKP's "
101cb93a386Sopenharmony_ci        "paths. An empty string performs no bisect. Only the SkPaths are bisected; all other draws "
102cb93a386Sopenharmony_ci        "are thrown out. This is useful for finding a reduced repo case for path drawing bugs.");
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_cistatic DEFINE_bool(ignoreSigInt, false, "ignore SIGINT signals during test execution");
105cb93a386Sopenharmony_ci
106cb93a386Sopenharmony_cistatic DEFINE_bool(checkF16, false, "Ensure that F16Norm pixels are clamped.");
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_cistatic DEFINE_string(colorImages, "",
109cb93a386Sopenharmony_ci              "List of images and/or directories to decode with color correction. "
110cb93a386Sopenharmony_ci              "A directory with no images is treated as a fatal error.");
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_cistatic DEFINE_bool2(veryVerbose, V, false, "tell individual tests to be verbose.");
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_cistatic DEFINE_bool(cpu, true, "Run CPU-bound work?");
115cb93a386Sopenharmony_cistatic DEFINE_bool(gpu, true, "Run GPU-bound work?");
116cb93a386Sopenharmony_cistatic DEFINE_bool(graphite, true, "Run Graphite work?");
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_cistatic DEFINE_bool(dryRun, false,
119cb93a386Sopenharmony_ci                   "just print the tests that would be run, without actually running them.");
120cb93a386Sopenharmony_ci
121cb93a386Sopenharmony_cistatic DEFINE_string(images, "",
122cb93a386Sopenharmony_ci                     "List of images and/or directories to decode. A directory with no images"
123cb93a386Sopenharmony_ci                     " is treated as a fatal error.");
124cb93a386Sopenharmony_ci
125cb93a386Sopenharmony_cistatic DEFINE_bool(simpleCodec, false,
126cb93a386Sopenharmony_ci                   "Runs of a subset of the codec tests, "
127cb93a386Sopenharmony_ci                   "with no scaling or subsetting, always using the canvas color type.");
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_cistatic DEFINE_string2(match, m, nullptr,
130cb93a386Sopenharmony_ci               "[~][^]substring[$] [...] of name to run.\n"
131cb93a386Sopenharmony_ci               "Multiple matches may be separated by spaces.\n"
132cb93a386Sopenharmony_ci               "~ causes a matching name to always be skipped\n"
133cb93a386Sopenharmony_ci               "^ requires the start of the name to match\n"
134cb93a386Sopenharmony_ci               "$ requires the end of the name to match\n"
135cb93a386Sopenharmony_ci               "^ and $ requires an exact match\n"
136cb93a386Sopenharmony_ci               "If a name does not match any list entry,\n"
137cb93a386Sopenharmony_ci               "it is skipped unless some list entry starts with ~");
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_cistatic DEFINE_bool2(quiet, q, false, "if true, don't print status updates.");
140cb93a386Sopenharmony_cistatic DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver.");
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_cistatic DEFINE_string(skps, "skps", "Directory to read skps from.");
143cb93a386Sopenharmony_cistatic DEFINE_string(lotties, "lotties", "Directory to read (Bodymovin) jsons from.");
144cb93a386Sopenharmony_cistatic DEFINE_string(rives, "rives", "Directory to read Rive/Flare files from.");
145cb93a386Sopenharmony_cistatic DEFINE_string(svgs, "", "Directory to read SVGs from, or a single SVG file.");
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_cistatic DEFINE_int_2(threads, j, -1,
148cb93a386Sopenharmony_ci               "Run threadsafe tests on a threadpool with this many extra threads, "
149cb93a386Sopenharmony_ci               "defaulting to one extra thread per core.");
150cb93a386Sopenharmony_ci
151cb93a386Sopenharmony_cistatic DEFINE_string(key, "",
152cb93a386Sopenharmony_ci                     "Space-separated key/value pairs to add to JSON identifying this builder.");
153cb93a386Sopenharmony_cistatic DEFINE_string(properties, "",
154cb93a386Sopenharmony_ci                     "Space-separated key/value pairs to add to JSON identifying this run.");
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_cistatic DEFINE_bool(rasterize_pdf, false, "Rasterize PDFs when possible.");
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_cistatic DEFINE_bool(runVerifiers, false,
159cb93a386Sopenharmony_ci                   "if true, run SkQP-style verification of GM-produced images.");
160cb93a386Sopenharmony_ci
161cb93a386Sopenharmony_ci#if defined(__MSVC_RUNTIME_CHECKS)
162cb93a386Sopenharmony_ci#include <rtcapi.h>
163cb93a386Sopenharmony_ciint RuntimeCheckErrorFunc(int errorType, const char* filename, int linenumber,
164cb93a386Sopenharmony_ci                          const char* moduleName, const char* fmt, ...) {
165cb93a386Sopenharmony_ci    va_list args;
166cb93a386Sopenharmony_ci    va_start(args, fmt);
167cb93a386Sopenharmony_ci    vfprintf(stderr, fmt, args);
168cb93a386Sopenharmony_ci    va_end(args);
169cb93a386Sopenharmony_ci
170cb93a386Sopenharmony_ci    SkDebugf("Line #%d\nFile: %s\nModule: %s\n",
171cb93a386Sopenharmony_ci             linenumber, filename ? filename : "Unknown", moduleName ? moduleName : "Unknwon");
172cb93a386Sopenharmony_ci    return 1;
173cb93a386Sopenharmony_ci}
174cb93a386Sopenharmony_ci#endif
175cb93a386Sopenharmony_ci
176cb93a386Sopenharmony_ciusing namespace DM;
177cb93a386Sopenharmony_ciusing sk_gpu_test::GrContextFactory;
178cb93a386Sopenharmony_ciusing sk_gpu_test::GLTestContext;
179cb93a386Sopenharmony_ciusing sk_gpu_test::ContextInfo;
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_cistatic FILE* gVLog;
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_citemplate <typename... Args>
186cb93a386Sopenharmony_cistatic void vlog(const char* fmt, Args&&... args) {
187cb93a386Sopenharmony_ci    if (gVLog) {
188cb93a386Sopenharmony_ci        fprintf(gVLog, fmt, args...);
189cb93a386Sopenharmony_ci        fflush(gVLog);
190cb93a386Sopenharmony_ci    }
191cb93a386Sopenharmony_ci}
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_citemplate <typename... Args>
194cb93a386Sopenharmony_cistatic void info(const char* fmt, Args&&... args) {
195cb93a386Sopenharmony_ci    vlog(fmt, args...);
196cb93a386Sopenharmony_ci    if (!FLAGS_quiet) {
197cb93a386Sopenharmony_ci        printf(fmt, args...);
198cb93a386Sopenharmony_ci    }
199cb93a386Sopenharmony_ci}
200cb93a386Sopenharmony_cistatic void info(const char* fmt) {
201cb93a386Sopenharmony_ci    if (!FLAGS_quiet) {
202cb93a386Sopenharmony_ci        printf("%s", fmt);  // Clang warns printf(fmt) is insecure.
203cb93a386Sopenharmony_ci    }
204cb93a386Sopenharmony_ci}
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_cistatic SkTArray<SkString>* gFailures = new SkTArray<SkString>;
207cb93a386Sopenharmony_ci
208cb93a386Sopenharmony_cistatic void fail(const SkString& err) {
209cb93a386Sopenharmony_ci    static SkSpinlock mutex;
210cb93a386Sopenharmony_ci    SkAutoSpinlock lock(mutex);
211cb93a386Sopenharmony_ci    SkDebugf("\n\nFAILURE: %s\n\n", err.c_str());
212cb93a386Sopenharmony_ci    gFailures->push_back(err);
213cb93a386Sopenharmony_ci}
214cb93a386Sopenharmony_ci
215cb93a386Sopenharmony_cistruct Running {
216cb93a386Sopenharmony_ci    SkString   id;
217cb93a386Sopenharmony_ci    SkThreadID thread;
218cb93a386Sopenharmony_ci
219cb93a386Sopenharmony_ci    void dump() const {
220cb93a386Sopenharmony_ci        info("\t%s\n", id.c_str());
221cb93a386Sopenharmony_ci    }
222cb93a386Sopenharmony_ci};
223cb93a386Sopenharmony_ci
224cb93a386Sopenharmony_cistatic void dump_json() {
225cb93a386Sopenharmony_ci    if (!FLAGS_writePath.isEmpty()) {
226cb93a386Sopenharmony_ci        JsonWriter::DumpJson(FLAGS_writePath[0], FLAGS_key, FLAGS_properties);
227cb93a386Sopenharmony_ci    }
228cb93a386Sopenharmony_ci}
229cb93a386Sopenharmony_ci
230cb93a386Sopenharmony_ci// We use a spinlock to make locking this in a signal handler _somewhat_ safe.
231cb93a386Sopenharmony_cistatic SkSpinlock*        gMutex = new SkSpinlock;
232cb93a386Sopenharmony_cistatic int                gPending;
233cb93a386Sopenharmony_cistatic SkTArray<Running>* gRunning = new SkTArray<Running>;
234cb93a386Sopenharmony_ci
235cb93a386Sopenharmony_cistatic void done(const char* config, const char* src, const char* srcOptions, const char* name) {
236cb93a386Sopenharmony_ci    SkString id = SkStringPrintf("%s %s %s %s", config, src, srcOptions, name);
237cb93a386Sopenharmony_ci    vlog("done  %s\n", id.c_str());
238cb93a386Sopenharmony_ci    int pending;
239cb93a386Sopenharmony_ci    {
240cb93a386Sopenharmony_ci        SkAutoSpinlock lock(*gMutex);
241cb93a386Sopenharmony_ci        for (int i = 0; i < gRunning->count(); i++) {
242cb93a386Sopenharmony_ci            if (gRunning->at(i).id == id) {
243cb93a386Sopenharmony_ci                gRunning->removeShuffle(i);
244cb93a386Sopenharmony_ci                break;
245cb93a386Sopenharmony_ci            }
246cb93a386Sopenharmony_ci        }
247cb93a386Sopenharmony_ci        pending = --gPending;
248cb93a386Sopenharmony_ci    }
249cb93a386Sopenharmony_ci
250cb93a386Sopenharmony_ci    // We write out dm.json file and print out a progress update every once in a while.
251cb93a386Sopenharmony_ci    // Notice this also handles the final dm.json and progress update when pending == 0.
252cb93a386Sopenharmony_ci    if (pending % 500 == 0) {
253cb93a386Sopenharmony_ci        dump_json();
254cb93a386Sopenharmony_ci
255cb93a386Sopenharmony_ci        int curr = sk_tools::getCurrResidentSetSizeMB(),
256cb93a386Sopenharmony_ci            peak = sk_tools::getMaxResidentSetSizeMB();
257cb93a386Sopenharmony_ci
258cb93a386Sopenharmony_ci        SkAutoSpinlock lock(*gMutex);
259cb93a386Sopenharmony_ci        info("\n%dMB RAM, %dMB peak, %d queued, %d active:\n",
260cb93a386Sopenharmony_ci             curr, peak, gPending - gRunning->count(), gRunning->count());
261cb93a386Sopenharmony_ci        for (auto& task : *gRunning) {
262cb93a386Sopenharmony_ci            task.dump();
263cb93a386Sopenharmony_ci        }
264cb93a386Sopenharmony_ci    }
265cb93a386Sopenharmony_ci}
266cb93a386Sopenharmony_ci
267cb93a386Sopenharmony_cistatic void start(const char* config, const char* src, const char* srcOptions, const char* name) {
268cb93a386Sopenharmony_ci    SkString id = SkStringPrintf("%s %s %s %s", config, src, srcOptions, name);
269cb93a386Sopenharmony_ci    vlog("start %s\n", id.c_str());
270cb93a386Sopenharmony_ci    SkAutoSpinlock lock(*gMutex);
271cb93a386Sopenharmony_ci    gRunning->push_back({id,SkGetThreadID()});
272cb93a386Sopenharmony_ci}
273cb93a386Sopenharmony_ci
274cb93a386Sopenharmony_cistatic void find_culprit() {
275cb93a386Sopenharmony_ci    // Assumes gMutex is locked.
276cb93a386Sopenharmony_ci    SkThreadID thisThread = SkGetThreadID();
277cb93a386Sopenharmony_ci    for (auto& task : *gRunning) {
278cb93a386Sopenharmony_ci        if (task.thread == thisThread) {
279cb93a386Sopenharmony_ci            info("Likely culprit:\n");
280cb93a386Sopenharmony_ci            task.dump();
281cb93a386Sopenharmony_ci        }
282cb93a386Sopenharmony_ci    }
283cb93a386Sopenharmony_ci}
284cb93a386Sopenharmony_ci
285cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_WIN)
286cb93a386Sopenharmony_ci    static LONG WINAPI crash_handler(EXCEPTION_POINTERS* e) {
287cb93a386Sopenharmony_ci        static const struct {
288cb93a386Sopenharmony_ci            const char* name;
289cb93a386Sopenharmony_ci            DWORD code;
290cb93a386Sopenharmony_ci        } kExceptions[] = {
291cb93a386Sopenharmony_ci        #define _(E) {#E, E}
292cb93a386Sopenharmony_ci            _(EXCEPTION_ACCESS_VIOLATION),
293cb93a386Sopenharmony_ci            _(EXCEPTION_BREAKPOINT),
294cb93a386Sopenharmony_ci            _(EXCEPTION_INT_DIVIDE_BY_ZERO),
295cb93a386Sopenharmony_ci            _(EXCEPTION_STACK_OVERFLOW),
296cb93a386Sopenharmony_ci            // TODO: more?
297cb93a386Sopenharmony_ci        #undef _
298cb93a386Sopenharmony_ci        };
299cb93a386Sopenharmony_ci
300cb93a386Sopenharmony_ci        SkAutoSpinlock lock(*gMutex);
301cb93a386Sopenharmony_ci
302cb93a386Sopenharmony_ci        const DWORD code = e->ExceptionRecord->ExceptionCode;
303cb93a386Sopenharmony_ci        info("\nCaught exception %u", code);
304cb93a386Sopenharmony_ci        for (const auto& exception : kExceptions) {
305cb93a386Sopenharmony_ci            if (exception.code == code) {
306cb93a386Sopenharmony_ci                info(" %s", exception.name);
307cb93a386Sopenharmony_ci            }
308cb93a386Sopenharmony_ci        }
309cb93a386Sopenharmony_ci        info(", was running:\n");
310cb93a386Sopenharmony_ci        for (auto& task : *gRunning) {
311cb93a386Sopenharmony_ci            task.dump();
312cb93a386Sopenharmony_ci        }
313cb93a386Sopenharmony_ci        find_culprit();
314cb93a386Sopenharmony_ci        fflush(stdout);
315cb93a386Sopenharmony_ci
316cb93a386Sopenharmony_ci        // Execute default exception handler... hopefully, exit.
317cb93a386Sopenharmony_ci        return EXCEPTION_EXECUTE_HANDLER;
318cb93a386Sopenharmony_ci    }
319cb93a386Sopenharmony_ci
320cb93a386Sopenharmony_ci    static void setup_crash_handler() {
321cb93a386Sopenharmony_ci        SetUnhandledExceptionFilter(crash_handler);
322cb93a386Sopenharmony_ci    }
323cb93a386Sopenharmony_ci#else
324cb93a386Sopenharmony_ci    #include <signal.h>
325cb93a386Sopenharmony_ci    #if !defined(SK_BUILD_FOR_ANDROID)
326cb93a386Sopenharmony_ci        // #include <execinfo.h>
327cb93a386Sopenharmony_ci
328cb93a386Sopenharmony_ci#endif
329cb93a386Sopenharmony_ci
330cb93a386Sopenharmony_ci    static constexpr int max_of() { return 0; }
331cb93a386Sopenharmony_ci    template <typename... Rest>
332cb93a386Sopenharmony_ci    static constexpr int max_of(int x, Rest... rest) {
333cb93a386Sopenharmony_ci        return x > max_of(rest...) ? x : max_of(rest...);
334cb93a386Sopenharmony_ci    }
335cb93a386Sopenharmony_ci
336cb93a386Sopenharmony_ci    static void (*previous_handler[max_of(SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGSEGV,SIGTERM)+1])(int);
337cb93a386Sopenharmony_ci
338cb93a386Sopenharmony_ci    static void crash_handler(int sig) {
339cb93a386Sopenharmony_ci        SkAutoSpinlock lock(*gMutex);
340cb93a386Sopenharmony_ci
341cb93a386Sopenharmony_ci        info("\nCaught signal %d [%s] (%dMB RAM, peak %dMB), was running:\n",
342cb93a386Sopenharmony_ci             sig, strsignal(sig),
343cb93a386Sopenharmony_ci             sk_tools::getCurrResidentSetSizeMB(), sk_tools::getMaxResidentSetSizeMB());
344cb93a386Sopenharmony_ci
345cb93a386Sopenharmony_ci        for (auto& task : *gRunning) {
346cb93a386Sopenharmony_ci            task.dump();
347cb93a386Sopenharmony_ci        }
348cb93a386Sopenharmony_ci        find_culprit();
349cb93a386Sopenharmony_ci
350cb93a386Sopenharmony_ci/*
351cb93a386Sopenharmony_ci    #if !defined(SK_BUILD_FOR_ANDROID)
352cb93a386Sopenharmony_ci        void* stack[128];
353cb93a386Sopenharmony_ci        int count = backtrace(stack, SK_ARRAY_COUNT(stack));
354cb93a386Sopenharmony_ci        char** symbols = backtrace_symbols(stack, count);
355cb93a386Sopenharmony_ci        info("\nStack trace:\n");
356cb93a386Sopenharmony_ci        for (int i = 0; i < count; i++) {
357cb93a386Sopenharmony_ci            info("    %s\n", symbols[i]);
358cb93a386Sopenharmony_ci        }
359cb93a386Sopenharmony_ci    #endif
360cb93a386Sopenharmony_ci*/
361cb93a386Sopenharmony_ci        fflush(stdout);
362cb93a386Sopenharmony_ci
363cb93a386Sopenharmony_ci        if (sig == SIGINT && FLAGS_ignoreSigInt) {
364cb93a386Sopenharmony_ci            info("Ignoring signal %d because of --ignoreSigInt.\n"
365cb93a386Sopenharmony_ci                 "This is probably a sign the bot is overloaded with work.\n", sig);
366cb93a386Sopenharmony_ci        } else {
367cb93a386Sopenharmony_ci            signal(sig, previous_handler[sig]);
368cb93a386Sopenharmony_ci            raise(sig);
369cb93a386Sopenharmony_ci        }
370cb93a386Sopenharmony_ci    }
371cb93a386Sopenharmony_ci
372cb93a386Sopenharmony_ci    static void setup_crash_handler() {
373cb93a386Sopenharmony_ci        const int kSignals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM };
374cb93a386Sopenharmony_ci        for (int sig : kSignals) {
375cb93a386Sopenharmony_ci            previous_handler[sig] = signal(sig, crash_handler);
376cb93a386Sopenharmony_ci        }
377cb93a386Sopenharmony_ci    }
378cb93a386Sopenharmony_ci#endif
379cb93a386Sopenharmony_ci
380cb93a386Sopenharmony_ci/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
381cb93a386Sopenharmony_ci
382cb93a386Sopenharmony_cistruct Gold : public SkString {
383cb93a386Sopenharmony_ci    Gold() : SkString("") {}
384cb93a386Sopenharmony_ci    Gold(const SkString& sink, const SkString& src,
385cb93a386Sopenharmony_ci         const SkString& srcOptions, const SkString& name,
386cb93a386Sopenharmony_ci         const SkString& md5)
387cb93a386Sopenharmony_ci        : SkString("") {
388cb93a386Sopenharmony_ci        this->append(sink);
389cb93a386Sopenharmony_ci        this->append(src);
390cb93a386Sopenharmony_ci        this->append(srcOptions);
391cb93a386Sopenharmony_ci        this->append(name);
392cb93a386Sopenharmony_ci        this->append(md5);
393cb93a386Sopenharmony_ci    }
394cb93a386Sopenharmony_ci    struct Hash {
395cb93a386Sopenharmony_ci        uint32_t operator()(const Gold& g) const {
396cb93a386Sopenharmony_ci            return SkGoodHash()((const SkString&)g);
397cb93a386Sopenharmony_ci        }
398cb93a386Sopenharmony_ci    };
399cb93a386Sopenharmony_ci};
400cb93a386Sopenharmony_cistatic SkTHashSet<Gold, Gold::Hash>* gGold = new SkTHashSet<Gold, Gold::Hash>;
401cb93a386Sopenharmony_ci
402cb93a386Sopenharmony_cistatic void add_gold(JsonWriter::BitmapResult r) {
403cb93a386Sopenharmony_ci    gGold->add(Gold(r.config, r.sourceType, r.sourceOptions, r.name, r.md5));
404cb93a386Sopenharmony_ci}
405cb93a386Sopenharmony_ci
406cb93a386Sopenharmony_cistatic void gather_gold() {
407cb93a386Sopenharmony_ci    if (!FLAGS_readPath.isEmpty()) {
408cb93a386Sopenharmony_ci        SkString path(FLAGS_readPath[0]);
409cb93a386Sopenharmony_ci        path.append("/dm.json");
410cb93a386Sopenharmony_ci        if (!JsonWriter::ReadJson(path.c_str(), add_gold)) {
411cb93a386Sopenharmony_ci            fail(SkStringPrintf("Couldn't read %s for golden results.", path.c_str()));
412cb93a386Sopenharmony_ci        }
413cb93a386Sopenharmony_ci    }
414cb93a386Sopenharmony_ci}
415cb93a386Sopenharmony_ci
416cb93a386Sopenharmony_ci/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
417cb93a386Sopenharmony_ci
418cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_WIN)
419cb93a386Sopenharmony_ci    static constexpr char kNewline[] = "\r\n";
420cb93a386Sopenharmony_ci#else
421cb93a386Sopenharmony_ci    static constexpr char kNewline[] = "\n";
422cb93a386Sopenharmony_ci#endif
423cb93a386Sopenharmony_ci
424cb93a386Sopenharmony_cistatic SkTHashSet<SkString>* gUninterestingHashes = new SkTHashSet<SkString>;
425cb93a386Sopenharmony_ci
426cb93a386Sopenharmony_cistatic void gather_uninteresting_hashes() {
427cb93a386Sopenharmony_ci    if (!FLAGS_uninterestingHashesFile.isEmpty()) {
428cb93a386Sopenharmony_ci        sk_sp<SkData> data(SkData::MakeFromFileName(FLAGS_uninterestingHashesFile[0]));
429cb93a386Sopenharmony_ci        if (!data) {
430cb93a386Sopenharmony_ci            info("WARNING: unable to read uninteresting hashes from %s\n",
431cb93a386Sopenharmony_ci                 FLAGS_uninterestingHashesFile[0]);
432cb93a386Sopenharmony_ci            return;
433cb93a386Sopenharmony_ci        }
434cb93a386Sopenharmony_ci
435cb93a386Sopenharmony_ci        // Copy to a string to make sure SkStrSplit has a terminating \0 to find.
436cb93a386Sopenharmony_ci        SkString contents((const char*)data->data(), data->size());
437cb93a386Sopenharmony_ci
438cb93a386Sopenharmony_ci        SkTArray<SkString> hashes;
439cb93a386Sopenharmony_ci        SkStrSplit(contents.c_str(), kNewline, &hashes);
440cb93a386Sopenharmony_ci        for (const SkString& hash : hashes) {
441cb93a386Sopenharmony_ci            gUninterestingHashes->add(hash);
442cb93a386Sopenharmony_ci        }
443cb93a386Sopenharmony_ci        info("FYI: loaded %d distinct uninteresting hashes from %d lines\n",
444cb93a386Sopenharmony_ci             gUninterestingHashes->count(), hashes.count());
445cb93a386Sopenharmony_ci    }
446cb93a386Sopenharmony_ci}
447cb93a386Sopenharmony_ci
448cb93a386Sopenharmony_ci/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
449cb93a386Sopenharmony_ci
450cb93a386Sopenharmony_cistruct TaggedSrc : public std::unique_ptr<Src> {
451cb93a386Sopenharmony_ci    SkString tag;
452cb93a386Sopenharmony_ci    SkString options;
453cb93a386Sopenharmony_ci};
454cb93a386Sopenharmony_ci
455cb93a386Sopenharmony_cistruct TaggedSink : public std::unique_ptr<Sink> {
456cb93a386Sopenharmony_ci    SkString tag;
457cb93a386Sopenharmony_ci};
458cb93a386Sopenharmony_ci
459cb93a386Sopenharmony_cistatic constexpr bool kMemcpyOK = true;
460cb93a386Sopenharmony_ci
461cb93a386Sopenharmony_cistatic SkTArray<TaggedSrc,  kMemcpyOK>* gSrcs  = new SkTArray<TaggedSrc,  kMemcpyOK>;
462cb93a386Sopenharmony_cistatic SkTArray<TaggedSink, kMemcpyOK>* gSinks = new SkTArray<TaggedSink, kMemcpyOK>;
463cb93a386Sopenharmony_ci
464cb93a386Sopenharmony_cistatic bool in_shard() {
465cb93a386Sopenharmony_ci    static int N = 0;
466cb93a386Sopenharmony_ci    return N++ % FLAGS_shards == FLAGS_shard;
467cb93a386Sopenharmony_ci}
468cb93a386Sopenharmony_ci
469cb93a386Sopenharmony_cistatic void push_src(const char* tag, ImplicitString options, Src* inSrc) {
470cb93a386Sopenharmony_ci    std::unique_ptr<Src> src(inSrc);
471cb93a386Sopenharmony_ci    if (in_shard() && FLAGS_src.contains(tag) &&
472cb93a386Sopenharmony_ci        !CommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) {
473cb93a386Sopenharmony_ci        TaggedSrc& s = gSrcs->push_back();
474cb93a386Sopenharmony_ci        s.reset(src.release());
475cb93a386Sopenharmony_ci        s.tag = tag;
476cb93a386Sopenharmony_ci        s.options = options;
477cb93a386Sopenharmony_ci    }
478cb93a386Sopenharmony_ci}
479cb93a386Sopenharmony_ci
480cb93a386Sopenharmony_cistatic void push_codec_src(Path path, CodecSrc::Mode mode, CodecSrc::DstColorType dstColorType,
481cb93a386Sopenharmony_ci        SkAlphaType dstAlphaType, float scale) {
482cb93a386Sopenharmony_ci    if (FLAGS_simpleCodec) {
483cb93a386Sopenharmony_ci        const bool simple = CodecSrc::kCodec_Mode == mode || CodecSrc::kAnimated_Mode == mode;
484cb93a386Sopenharmony_ci        if (!simple || dstColorType != CodecSrc::kGetFromCanvas_DstColorType || scale != 1.0f) {
485cb93a386Sopenharmony_ci            // Only decode in the simple case.
486cb93a386Sopenharmony_ci            return;
487cb93a386Sopenharmony_ci        }
488cb93a386Sopenharmony_ci    }
489cb93a386Sopenharmony_ci    SkString folder;
490cb93a386Sopenharmony_ci    switch (mode) {
491cb93a386Sopenharmony_ci        case CodecSrc::kCodec_Mode:
492cb93a386Sopenharmony_ci            folder.append("codec");
493cb93a386Sopenharmony_ci            break;
494cb93a386Sopenharmony_ci        case CodecSrc::kCodecZeroInit_Mode:
495cb93a386Sopenharmony_ci            folder.append("codec_zero_init");
496cb93a386Sopenharmony_ci            break;
497cb93a386Sopenharmony_ci        case CodecSrc::kScanline_Mode:
498cb93a386Sopenharmony_ci            folder.append("scanline");
499cb93a386Sopenharmony_ci            break;
500cb93a386Sopenharmony_ci        case CodecSrc::kStripe_Mode:
501cb93a386Sopenharmony_ci            folder.append("stripe");
502cb93a386Sopenharmony_ci            break;
503cb93a386Sopenharmony_ci        case CodecSrc::kCroppedScanline_Mode:
504cb93a386Sopenharmony_ci            folder.append("crop");
505cb93a386Sopenharmony_ci            break;
506cb93a386Sopenharmony_ci        case CodecSrc::kSubset_Mode:
507cb93a386Sopenharmony_ci            folder.append("codec_subset");
508cb93a386Sopenharmony_ci            break;
509cb93a386Sopenharmony_ci        case CodecSrc::kAnimated_Mode:
510cb93a386Sopenharmony_ci            folder.append("codec_animated");
511cb93a386Sopenharmony_ci            break;
512cb93a386Sopenharmony_ci    }
513cb93a386Sopenharmony_ci
514cb93a386Sopenharmony_ci    switch (dstColorType) {
515cb93a386Sopenharmony_ci        case CodecSrc::kGrayscale_Always_DstColorType:
516cb93a386Sopenharmony_ci            folder.append("_kGray8");
517cb93a386Sopenharmony_ci            break;
518cb93a386Sopenharmony_ci        case CodecSrc::kNonNative8888_Always_DstColorType:
519cb93a386Sopenharmony_ci            folder.append("_kNonNative");
520cb93a386Sopenharmony_ci            break;
521cb93a386Sopenharmony_ci        default:
522cb93a386Sopenharmony_ci            break;
523cb93a386Sopenharmony_ci    }
524cb93a386Sopenharmony_ci
525cb93a386Sopenharmony_ci    switch (dstAlphaType) {
526cb93a386Sopenharmony_ci        case kPremul_SkAlphaType:
527cb93a386Sopenharmony_ci            folder.append("_premul");
528cb93a386Sopenharmony_ci            break;
529cb93a386Sopenharmony_ci        case kUnpremul_SkAlphaType:
530cb93a386Sopenharmony_ci            folder.append("_unpremul");
531cb93a386Sopenharmony_ci            break;
532cb93a386Sopenharmony_ci        default:
533cb93a386Sopenharmony_ci            break;
534cb93a386Sopenharmony_ci    }
535cb93a386Sopenharmony_ci
536cb93a386Sopenharmony_ci    if (1.0f != scale) {
537cb93a386Sopenharmony_ci        folder.appendf("_%.3f", scale);
538cb93a386Sopenharmony_ci    }
539cb93a386Sopenharmony_ci
540cb93a386Sopenharmony_ci    CodecSrc* src = new CodecSrc(path, mode, dstColorType, dstAlphaType, scale);
541cb93a386Sopenharmony_ci    push_src("image", folder, src);
542cb93a386Sopenharmony_ci}
543cb93a386Sopenharmony_ci
544cb93a386Sopenharmony_cistatic void push_android_codec_src(Path path, CodecSrc::DstColorType dstColorType,
545cb93a386Sopenharmony_ci        SkAlphaType dstAlphaType, int sampleSize) {
546cb93a386Sopenharmony_ci    SkString folder;
547cb93a386Sopenharmony_ci    folder.append("scaled_codec");
548cb93a386Sopenharmony_ci
549cb93a386Sopenharmony_ci    switch (dstColorType) {
550cb93a386Sopenharmony_ci        case CodecSrc::kGrayscale_Always_DstColorType:
551cb93a386Sopenharmony_ci            folder.append("_kGray8");
552cb93a386Sopenharmony_ci            break;
553cb93a386Sopenharmony_ci        case CodecSrc::kNonNative8888_Always_DstColorType:
554cb93a386Sopenharmony_ci            folder.append("_kNonNative");
555cb93a386Sopenharmony_ci            break;
556cb93a386Sopenharmony_ci        default:
557cb93a386Sopenharmony_ci            break;
558cb93a386Sopenharmony_ci    }
559cb93a386Sopenharmony_ci
560cb93a386Sopenharmony_ci    switch (dstAlphaType) {
561cb93a386Sopenharmony_ci        case kPremul_SkAlphaType:
562cb93a386Sopenharmony_ci            folder.append("_premul");
563cb93a386Sopenharmony_ci            break;
564cb93a386Sopenharmony_ci        case kUnpremul_SkAlphaType:
565cb93a386Sopenharmony_ci            folder.append("_unpremul");
566cb93a386Sopenharmony_ci            break;
567cb93a386Sopenharmony_ci        default:
568cb93a386Sopenharmony_ci            break;
569cb93a386Sopenharmony_ci    }
570cb93a386Sopenharmony_ci
571cb93a386Sopenharmony_ci    if (1 != sampleSize) {
572cb93a386Sopenharmony_ci        folder.appendf("_%.3f", 1.0f / (float) sampleSize);
573cb93a386Sopenharmony_ci    }
574cb93a386Sopenharmony_ci
575cb93a386Sopenharmony_ci    AndroidCodecSrc* src = new AndroidCodecSrc(path, dstColorType, dstAlphaType, sampleSize);
576cb93a386Sopenharmony_ci    push_src("image", folder, src);
577cb93a386Sopenharmony_ci}
578cb93a386Sopenharmony_ci
579cb93a386Sopenharmony_cistatic void push_image_gen_src(Path path, ImageGenSrc::Mode mode, SkAlphaType alphaType, bool isGpu)
580cb93a386Sopenharmony_ci{
581cb93a386Sopenharmony_ci    SkString folder;
582cb93a386Sopenharmony_ci    switch (mode) {
583cb93a386Sopenharmony_ci        case ImageGenSrc::kCodec_Mode:
584cb93a386Sopenharmony_ci            folder.append("gen_codec");
585cb93a386Sopenharmony_ci            break;
586cb93a386Sopenharmony_ci        case ImageGenSrc::kPlatform_Mode:
587cb93a386Sopenharmony_ci            folder.append("gen_platform");
588cb93a386Sopenharmony_ci            break;
589cb93a386Sopenharmony_ci    }
590cb93a386Sopenharmony_ci
591cb93a386Sopenharmony_ci    if (isGpu) {
592cb93a386Sopenharmony_ci        folder.append("_gpu");
593cb93a386Sopenharmony_ci    } else {
594cb93a386Sopenharmony_ci        switch (alphaType) {
595cb93a386Sopenharmony_ci            case kOpaque_SkAlphaType:
596cb93a386Sopenharmony_ci                folder.append("_opaque");
597cb93a386Sopenharmony_ci                break;
598cb93a386Sopenharmony_ci            case kPremul_SkAlphaType:
599cb93a386Sopenharmony_ci                folder.append("_premul");
600cb93a386Sopenharmony_ci                break;
601cb93a386Sopenharmony_ci            case kUnpremul_SkAlphaType:
602cb93a386Sopenharmony_ci                folder.append("_unpremul");
603cb93a386Sopenharmony_ci                break;
604cb93a386Sopenharmony_ci            default:
605cb93a386Sopenharmony_ci                break;
606cb93a386Sopenharmony_ci        }
607cb93a386Sopenharmony_ci    }
608cb93a386Sopenharmony_ci
609cb93a386Sopenharmony_ci    ImageGenSrc* src = new ImageGenSrc(path, mode, alphaType, isGpu);
610cb93a386Sopenharmony_ci    push_src("image", folder, src);
611cb93a386Sopenharmony_ci}
612cb93a386Sopenharmony_ci
613cb93a386Sopenharmony_ci#ifdef SK_ENABLE_ANDROID_UTILS
614cb93a386Sopenharmony_cistatic void push_brd_src(Path path, CodecSrc::DstColorType dstColorType, BRDSrc::Mode mode,
615cb93a386Sopenharmony_ci        uint32_t sampleSize) {
616cb93a386Sopenharmony_ci    SkString folder("brd_android_codec");
617cb93a386Sopenharmony_ci    switch (mode) {
618cb93a386Sopenharmony_ci        case BRDSrc::kFullImage_Mode:
619cb93a386Sopenharmony_ci            break;
620cb93a386Sopenharmony_ci        case BRDSrc::kDivisor_Mode:
621cb93a386Sopenharmony_ci            folder.append("_divisor");
622cb93a386Sopenharmony_ci            break;
623cb93a386Sopenharmony_ci        default:
624cb93a386Sopenharmony_ci            SkASSERT(false);
625cb93a386Sopenharmony_ci            return;
626cb93a386Sopenharmony_ci    }
627cb93a386Sopenharmony_ci
628cb93a386Sopenharmony_ci    switch (dstColorType) {
629cb93a386Sopenharmony_ci        case CodecSrc::kGetFromCanvas_DstColorType:
630cb93a386Sopenharmony_ci            break;
631cb93a386Sopenharmony_ci        case CodecSrc::kGrayscale_Always_DstColorType:
632cb93a386Sopenharmony_ci            folder.append("_kGray");
633cb93a386Sopenharmony_ci            break;
634cb93a386Sopenharmony_ci        default:
635cb93a386Sopenharmony_ci            SkASSERT(false);
636cb93a386Sopenharmony_ci            return;
637cb93a386Sopenharmony_ci    }
638cb93a386Sopenharmony_ci
639cb93a386Sopenharmony_ci    if (1 != sampleSize) {
640cb93a386Sopenharmony_ci        folder.appendf("_%.3f", 1.0f / (float) sampleSize);
641cb93a386Sopenharmony_ci    }
642cb93a386Sopenharmony_ci
643cb93a386Sopenharmony_ci    BRDSrc* src = new BRDSrc(path, mode, dstColorType, sampleSize);
644cb93a386Sopenharmony_ci    push_src("image", folder, src);
645cb93a386Sopenharmony_ci}
646cb93a386Sopenharmony_ci
647cb93a386Sopenharmony_cistatic void push_brd_srcs(Path path, bool gray) {
648cb93a386Sopenharmony_ci    if (gray) {
649cb93a386Sopenharmony_ci        // Only run grayscale to one sampleSize and Mode. Though interesting
650cb93a386Sopenharmony_ci        // to test grayscale, it should not reveal anything across various
651cb93a386Sopenharmony_ci        // sampleSizes and Modes
652cb93a386Sopenharmony_ci        // Arbitrarily choose Mode and sampleSize.
653cb93a386Sopenharmony_ci        push_brd_src(path, CodecSrc::kGrayscale_Always_DstColorType,
654cb93a386Sopenharmony_ci                     BRDSrc::kFullImage_Mode, 2);
655cb93a386Sopenharmony_ci    }
656cb93a386Sopenharmony_ci
657cb93a386Sopenharmony_ci    // Test on a variety of sampleSizes, making sure to include:
658cb93a386Sopenharmony_ci    // - 2, 4, and 8, which are natively supported by jpeg
659cb93a386Sopenharmony_ci    // - multiples of 2 which are not divisible by 4 (analogous for 4)
660cb93a386Sopenharmony_ci    // - larger powers of two, since BRD clients generally use powers of 2
661cb93a386Sopenharmony_ci    // We will only produce output for the larger sizes on large images.
662cb93a386Sopenharmony_ci    const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 64 };
663cb93a386Sopenharmony_ci
664cb93a386Sopenharmony_ci    const BRDSrc::Mode modes[] = { BRDSrc::kFullImage_Mode, BRDSrc::kDivisor_Mode, };
665cb93a386Sopenharmony_ci
666cb93a386Sopenharmony_ci    for (uint32_t sampleSize : sampleSizes) {
667cb93a386Sopenharmony_ci        for (BRDSrc::Mode mode : modes) {
668cb93a386Sopenharmony_ci            push_brd_src(path, CodecSrc::kGetFromCanvas_DstColorType, mode, sampleSize);
669cb93a386Sopenharmony_ci        }
670cb93a386Sopenharmony_ci    }
671cb93a386Sopenharmony_ci}
672cb93a386Sopenharmony_ci#endif // SK_ENABLE_ANDROID_UTILS
673cb93a386Sopenharmony_ci
674cb93a386Sopenharmony_cistatic void push_codec_srcs(Path path) {
675cb93a386Sopenharmony_ci    sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
676cb93a386Sopenharmony_ci    if (!encoded) {
677cb93a386Sopenharmony_ci        info("Couldn't read %s.", path.c_str());
678cb93a386Sopenharmony_ci        return;
679cb93a386Sopenharmony_ci    }
680cb93a386Sopenharmony_ci    std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(encoded);
681cb93a386Sopenharmony_ci    if (nullptr == codec) {
682cb93a386Sopenharmony_ci        info("Couldn't create codec for %s.", path.c_str());
683cb93a386Sopenharmony_ci        return;
684cb93a386Sopenharmony_ci    }
685cb93a386Sopenharmony_ci
686cb93a386Sopenharmony_ci    // native scaling is only supported by WEBP and JPEG
687cb93a386Sopenharmony_ci    bool supportsNativeScaling = false;
688cb93a386Sopenharmony_ci
689cb93a386Sopenharmony_ci    SkTArray<CodecSrc::Mode> nativeModes;
690cb93a386Sopenharmony_ci    nativeModes.push_back(CodecSrc::kCodec_Mode);
691cb93a386Sopenharmony_ci    nativeModes.push_back(CodecSrc::kCodecZeroInit_Mode);
692cb93a386Sopenharmony_ci    switch (codec->getEncodedFormat()) {
693cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kJPEG:
694cb93a386Sopenharmony_ci            nativeModes.push_back(CodecSrc::kScanline_Mode);
695cb93a386Sopenharmony_ci            nativeModes.push_back(CodecSrc::kStripe_Mode);
696cb93a386Sopenharmony_ci            nativeModes.push_back(CodecSrc::kCroppedScanline_Mode);
697cb93a386Sopenharmony_ci            supportsNativeScaling = true;
698cb93a386Sopenharmony_ci            break;
699cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kWEBP:
700cb93a386Sopenharmony_ci            nativeModes.push_back(CodecSrc::kSubset_Mode);
701cb93a386Sopenharmony_ci            supportsNativeScaling = true;
702cb93a386Sopenharmony_ci            break;
703cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kDNG:
704cb93a386Sopenharmony_ci            break;
705cb93a386Sopenharmony_ci        default:
706cb93a386Sopenharmony_ci            nativeModes.push_back(CodecSrc::kScanline_Mode);
707cb93a386Sopenharmony_ci            break;
708cb93a386Sopenharmony_ci    }
709cb93a386Sopenharmony_ci
710cb93a386Sopenharmony_ci    SkTArray<CodecSrc::DstColorType> colorTypes;
711cb93a386Sopenharmony_ci    colorTypes.push_back(CodecSrc::kGetFromCanvas_DstColorType);
712cb93a386Sopenharmony_ci    colorTypes.push_back(CodecSrc::kNonNative8888_Always_DstColorType);
713cb93a386Sopenharmony_ci    switch (codec->getInfo().colorType()) {
714cb93a386Sopenharmony_ci        case kGray_8_SkColorType:
715cb93a386Sopenharmony_ci            colorTypes.push_back(CodecSrc::kGrayscale_Always_DstColorType);
716cb93a386Sopenharmony_ci            break;
717cb93a386Sopenharmony_ci        default:
718cb93a386Sopenharmony_ci            break;
719cb93a386Sopenharmony_ci    }
720cb93a386Sopenharmony_ci
721cb93a386Sopenharmony_ci    SkTArray<SkAlphaType> alphaModes;
722cb93a386Sopenharmony_ci    alphaModes.push_back(kPremul_SkAlphaType);
723cb93a386Sopenharmony_ci    if (codec->getInfo().alphaType() != kOpaque_SkAlphaType) {
724cb93a386Sopenharmony_ci        alphaModes.push_back(kUnpremul_SkAlphaType);
725cb93a386Sopenharmony_ci    }
726cb93a386Sopenharmony_ci
727cb93a386Sopenharmony_ci    for (CodecSrc::Mode mode : nativeModes) {
728cb93a386Sopenharmony_ci        for (CodecSrc::DstColorType colorType : colorTypes) {
729cb93a386Sopenharmony_ci            for (SkAlphaType alphaType : alphaModes) {
730cb93a386Sopenharmony_ci                // Only test kCroppedScanline_Mode when the alpha type is premul.  The test is
731cb93a386Sopenharmony_ci                // slow and won't be interestingly different with different alpha types.
732cb93a386Sopenharmony_ci                if (CodecSrc::kCroppedScanline_Mode == mode &&
733cb93a386Sopenharmony_ci                        kPremul_SkAlphaType != alphaType) {
734cb93a386Sopenharmony_ci                    continue;
735cb93a386Sopenharmony_ci                }
736cb93a386Sopenharmony_ci
737cb93a386Sopenharmony_ci                push_codec_src(path, mode, colorType, alphaType, 1.0f);
738cb93a386Sopenharmony_ci
739cb93a386Sopenharmony_ci                // Skip kNonNative on different native scales.  It won't be interestingly
740cb93a386Sopenharmony_ci                // different.
741cb93a386Sopenharmony_ci                if (supportsNativeScaling &&
742cb93a386Sopenharmony_ci                        CodecSrc::kNonNative8888_Always_DstColorType == colorType) {
743cb93a386Sopenharmony_ci                    // Native Scales
744cb93a386Sopenharmony_ci                    // SkJpegCodec natively supports scaling to the following:
745cb93a386Sopenharmony_ci                    for (auto scale : { 0.125f, 0.25f, 0.375f, 0.5f, 0.625f, 0.750f, 0.875f }) {
746cb93a386Sopenharmony_ci                        push_codec_src(path, mode, colorType, alphaType, scale);
747cb93a386Sopenharmony_ci                    }
748cb93a386Sopenharmony_ci                }
749cb93a386Sopenharmony_ci            }
750cb93a386Sopenharmony_ci        }
751cb93a386Sopenharmony_ci    }
752cb93a386Sopenharmony_ci
753cb93a386Sopenharmony_ci    {
754cb93a386Sopenharmony_ci        std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
755cb93a386Sopenharmony_ci        if (frameInfos.size() > 1) {
756cb93a386Sopenharmony_ci            for (auto dstCT : { CodecSrc::kNonNative8888_Always_DstColorType,
757cb93a386Sopenharmony_ci                    CodecSrc::kGetFromCanvas_DstColorType }) {
758cb93a386Sopenharmony_ci                for (auto at : { kUnpremul_SkAlphaType, kPremul_SkAlphaType }) {
759cb93a386Sopenharmony_ci                    push_codec_src(path, CodecSrc::kAnimated_Mode, dstCT, at, 1.0f);
760cb93a386Sopenharmony_ci                }
761cb93a386Sopenharmony_ci            }
762cb93a386Sopenharmony_ci            for (float scale : { .5f, .33f }) {
763cb93a386Sopenharmony_ci                push_codec_src(path, CodecSrc::kAnimated_Mode, CodecSrc::kGetFromCanvas_DstColorType,
764cb93a386Sopenharmony_ci                               kPremul_SkAlphaType, scale);
765cb93a386Sopenharmony_ci            }
766cb93a386Sopenharmony_ci        }
767cb93a386Sopenharmony_ci
768cb93a386Sopenharmony_ci    }
769cb93a386Sopenharmony_ci
770cb93a386Sopenharmony_ci    if (FLAGS_simpleCodec) {
771cb93a386Sopenharmony_ci        return;
772cb93a386Sopenharmony_ci    }
773cb93a386Sopenharmony_ci
774cb93a386Sopenharmony_ci    const int sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
775cb93a386Sopenharmony_ci
776cb93a386Sopenharmony_ci    for (int sampleSize : sampleSizes) {
777cb93a386Sopenharmony_ci        for (CodecSrc::DstColorType colorType : colorTypes) {
778cb93a386Sopenharmony_ci            for (SkAlphaType alphaType : alphaModes) {
779cb93a386Sopenharmony_ci                // We can exercise all of the kNonNative support code in the swizzler with just a
780cb93a386Sopenharmony_ci                // few sample sizes.  Skip the rest.
781cb93a386Sopenharmony_ci                if (CodecSrc::kNonNative8888_Always_DstColorType == colorType && sampleSize > 3) {
782cb93a386Sopenharmony_ci                    continue;
783cb93a386Sopenharmony_ci                }
784cb93a386Sopenharmony_ci
785cb93a386Sopenharmony_ci                push_android_codec_src(path, colorType, alphaType, sampleSize);
786cb93a386Sopenharmony_ci            }
787cb93a386Sopenharmony_ci        }
788cb93a386Sopenharmony_ci    }
789cb93a386Sopenharmony_ci
790cb93a386Sopenharmony_ci    const char* ext = strrchr(path.c_str(), '.');
791cb93a386Sopenharmony_ci    if (ext) {
792cb93a386Sopenharmony_ci        ext++;
793cb93a386Sopenharmony_ci
794cb93a386Sopenharmony_ci        static const char* const rawExts[] = {
795cb93a386Sopenharmony_ci            "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw",
796cb93a386Sopenharmony_ci            "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW",
797cb93a386Sopenharmony_ci        };
798cb93a386Sopenharmony_ci        for (const char* rawExt : rawExts) {
799cb93a386Sopenharmony_ci            if (0 == strcmp(rawExt, ext)) {
800cb93a386Sopenharmony_ci                // RAW is not supported by image generator (skbug.com/5079) or BRD.
801cb93a386Sopenharmony_ci                return;
802cb93a386Sopenharmony_ci            }
803cb93a386Sopenharmony_ci        }
804cb93a386Sopenharmony_ci
805cb93a386Sopenharmony_ci#ifdef SK_ENABLE_ANDROID_UTILS
806cb93a386Sopenharmony_ci        static const char* const brdExts[] = {
807cb93a386Sopenharmony_ci            "jpg", "jpeg", "png", "webp",
808cb93a386Sopenharmony_ci            "JPG", "JPEG", "PNG", "WEBP",
809cb93a386Sopenharmony_ci        };
810cb93a386Sopenharmony_ci        for (const char* brdExt : brdExts) {
811cb93a386Sopenharmony_ci            if (0 == strcmp(brdExt, ext)) {
812cb93a386Sopenharmony_ci                bool gray = codec->getInfo().colorType() == kGray_8_SkColorType;
813cb93a386Sopenharmony_ci                push_brd_srcs(path, gray);
814cb93a386Sopenharmony_ci                break;
815cb93a386Sopenharmony_ci            }
816cb93a386Sopenharmony_ci        }
817cb93a386Sopenharmony_ci#endif
818cb93a386Sopenharmony_ci    }
819cb93a386Sopenharmony_ci
820cb93a386Sopenharmony_ci    // Push image generator GPU test.
821cb93a386Sopenharmony_ci    push_image_gen_src(path, ImageGenSrc::kCodec_Mode, codec->getInfo().alphaType(), true);
822cb93a386Sopenharmony_ci
823cb93a386Sopenharmony_ci    // Push image generator CPU tests.
824cb93a386Sopenharmony_ci    for (SkAlphaType alphaType : alphaModes) {
825cb93a386Sopenharmony_ci        push_image_gen_src(path, ImageGenSrc::kCodec_Mode, alphaType, false);
826cb93a386Sopenharmony_ci
827cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
828cb93a386Sopenharmony_ci        if (SkEncodedImageFormat::kWEBP != codec->getEncodedFormat() &&
829cb93a386Sopenharmony_ci            SkEncodedImageFormat::kWBMP != codec->getEncodedFormat() &&
830cb93a386Sopenharmony_ci            kUnpremul_SkAlphaType != alphaType)
831cb93a386Sopenharmony_ci        {
832cb93a386Sopenharmony_ci            push_image_gen_src(path, ImageGenSrc::kPlatform_Mode, alphaType, false);
833cb93a386Sopenharmony_ci        }
834cb93a386Sopenharmony_ci#elif defined(SK_BUILD_FOR_WIN)
835cb93a386Sopenharmony_ci        if (SkEncodedImageFormat::kWEBP != codec->getEncodedFormat() &&
836cb93a386Sopenharmony_ci            SkEncodedImageFormat::kWBMP != codec->getEncodedFormat())
837cb93a386Sopenharmony_ci        {
838cb93a386Sopenharmony_ci            push_image_gen_src(path, ImageGenSrc::kPlatform_Mode, alphaType, false);
839cb93a386Sopenharmony_ci        }
840cb93a386Sopenharmony_ci#elif defined(SK_ENABLE_NDK_IMAGES)
841cb93a386Sopenharmony_ci        push_image_gen_src(path, ImageGenSrc::kPlatform_Mode, alphaType, false);
842cb93a386Sopenharmony_ci#endif
843cb93a386Sopenharmony_ci    }
844cb93a386Sopenharmony_ci}
845cb93a386Sopenharmony_ci
846cb93a386Sopenharmony_citemplate <typename T>
847cb93a386Sopenharmony_civoid gather_file_srcs(const CommandLineFlags::StringArray& flags,
848cb93a386Sopenharmony_ci                      const char*                          ext,
849cb93a386Sopenharmony_ci                      const char*                          src_name = nullptr) {
850cb93a386Sopenharmony_ci    if (!src_name) {
851cb93a386Sopenharmony_ci        // With the exception of Lottie files, the source name is the extension.
852cb93a386Sopenharmony_ci        src_name = ext;
853cb93a386Sopenharmony_ci    }
854cb93a386Sopenharmony_ci
855cb93a386Sopenharmony_ci    for (int i = 0; i < flags.count(); i++) {
856cb93a386Sopenharmony_ci        const char* path = flags[i];
857cb93a386Sopenharmony_ci        if (sk_isdir(path)) {
858cb93a386Sopenharmony_ci            SkOSFile::Iter it(path, ext);
859cb93a386Sopenharmony_ci            for (SkString file; it.next(&file); ) {
860cb93a386Sopenharmony_ci                push_src(src_name, "", new T(SkOSPath::Join(path, file.c_str())));
861cb93a386Sopenharmony_ci            }
862cb93a386Sopenharmony_ci        } else {
863cb93a386Sopenharmony_ci            push_src(src_name, "", new T(path));
864cb93a386Sopenharmony_ci        }
865cb93a386Sopenharmony_ci    }
866cb93a386Sopenharmony_ci}
867cb93a386Sopenharmony_ci
868cb93a386Sopenharmony_cistatic bool gather_srcs() {
869cb93a386Sopenharmony_ci    for (skiagm::GMFactory f : skiagm::GMRegistry::Range()) {
870cb93a386Sopenharmony_ci        push_src("gm", "", new GMSrc(f));
871cb93a386Sopenharmony_ci    }
872cb93a386Sopenharmony_ci
873cb93a386Sopenharmony_ci    gather_file_srcs<SKPSrc>(FLAGS_skps, "skp");
874cb93a386Sopenharmony_ci    gather_file_srcs<MSKPSrc>(FLAGS_mskps, "mskp");
875cb93a386Sopenharmony_ci#if defined(SK_ENABLE_SKOTTIE)
876cb93a386Sopenharmony_ci    gather_file_srcs<SkottieSrc>(FLAGS_lotties, "json", "lottie");
877cb93a386Sopenharmony_ci#endif
878cb93a386Sopenharmony_ci#if defined(SK_ENABLE_SKRIVE)
879cb93a386Sopenharmony_ci    gather_file_srcs<SkRiveSrc>(FLAGS_rives, "flr", "rive");
880cb93a386Sopenharmony_ci#endif
881cb93a386Sopenharmony_ci#if defined(SK_ENABLE_SVG)
882cb93a386Sopenharmony_ci    gather_file_srcs<SVGSrc>(FLAGS_svgs, "svg");
883cb93a386Sopenharmony_ci#endif
884cb93a386Sopenharmony_ci    if (!FLAGS_bisect.isEmpty()) {
885cb93a386Sopenharmony_ci        // An empty l/r trail string will draw all the paths.
886cb93a386Sopenharmony_ci        push_src("bisect", "",
887cb93a386Sopenharmony_ci                 new BisectSrc(FLAGS_bisect[0], FLAGS_bisect.count() > 1 ? FLAGS_bisect[1] : ""));
888cb93a386Sopenharmony_ci    }
889cb93a386Sopenharmony_ci
890cb93a386Sopenharmony_ci    SkTArray<SkString> images;
891cb93a386Sopenharmony_ci    if (!CommonFlags::CollectImages(FLAGS_images, &images)) {
892cb93a386Sopenharmony_ci        return false;
893cb93a386Sopenharmony_ci    }
894cb93a386Sopenharmony_ci
895cb93a386Sopenharmony_ci    for (const SkString& image : images) {
896cb93a386Sopenharmony_ci        push_codec_srcs(image);
897cb93a386Sopenharmony_ci    }
898cb93a386Sopenharmony_ci
899cb93a386Sopenharmony_ci    SkTArray<SkString> colorImages;
900cb93a386Sopenharmony_ci    if (!CommonFlags::CollectImages(FLAGS_colorImages, &colorImages)) {
901cb93a386Sopenharmony_ci        return false;
902cb93a386Sopenharmony_ci    }
903cb93a386Sopenharmony_ci
904cb93a386Sopenharmony_ci    for (const SkString& colorImage : colorImages) {
905cb93a386Sopenharmony_ci        push_src("colorImage", "decode_native", new ColorCodecSrc(colorImage, false));
906cb93a386Sopenharmony_ci        push_src("colorImage", "decode_to_dst", new ColorCodecSrc(colorImage,  true));
907cb93a386Sopenharmony_ci    }
908cb93a386Sopenharmony_ci
909cb93a386Sopenharmony_ci    return true;
910cb93a386Sopenharmony_ci}
911cb93a386Sopenharmony_ci
912cb93a386Sopenharmony_cistatic void push_sink(const SkCommandLineConfig& config, Sink* s) {
913cb93a386Sopenharmony_ci    std::unique_ptr<Sink> sink(s);
914cb93a386Sopenharmony_ci
915cb93a386Sopenharmony_ci    // Try a simple Src as a canary.  If it fails, skip this sink.
916cb93a386Sopenharmony_ci    struct : public Src {
917cb93a386Sopenharmony_ci        Result draw(GrDirectContext*, SkCanvas* c) const override {
918cb93a386Sopenharmony_ci            c->drawRect(SkRect::MakeWH(1,1), SkPaint());
919cb93a386Sopenharmony_ci            return Result::Ok();
920cb93a386Sopenharmony_ci        }
921cb93a386Sopenharmony_ci        SkISize size() const override { return SkISize::Make(16, 16); }
922cb93a386Sopenharmony_ci        Name name() const override { return "justOneRect"; }
923cb93a386Sopenharmony_ci    } justOneRect;
924cb93a386Sopenharmony_ci
925cb93a386Sopenharmony_ci    SkBitmap bitmap;
926cb93a386Sopenharmony_ci    SkDynamicMemoryWStream stream;
927cb93a386Sopenharmony_ci    SkString log;
928cb93a386Sopenharmony_ci    Result result = sink->draw(justOneRect, &bitmap, &stream, &log);
929cb93a386Sopenharmony_ci    if (result.isFatal()) {
930cb93a386Sopenharmony_ci        info("Could not run %s: %s\n", config.getTag().c_str(), result.c_str());
931cb93a386Sopenharmony_ci        exit(1);
932cb93a386Sopenharmony_ci    }
933cb93a386Sopenharmony_ci
934cb93a386Sopenharmony_ci    TaggedSink& ts = gSinks->push_back();
935cb93a386Sopenharmony_ci    ts.reset(sink.release());
936cb93a386Sopenharmony_ci    ts.tag = config.getTag();
937cb93a386Sopenharmony_ci}
938cb93a386Sopenharmony_ci
939cb93a386Sopenharmony_cistatic Sink* create_sink(const GrContextOptions& grCtxOptions, const SkCommandLineConfig* config) {
940cb93a386Sopenharmony_ci    if (FLAGS_gpu) {
941cb93a386Sopenharmony_ci        if (const SkCommandLineConfigGpu* gpuConfig = config->asConfigGpu()) {
942cb93a386Sopenharmony_ci            GrContextFactory testFactory(grCtxOptions);
943cb93a386Sopenharmony_ci            if (!testFactory.get(gpuConfig->getContextType(), gpuConfig->getContextOverrides())) {
944cb93a386Sopenharmony_ci                info("WARNING: can not create GPU context for config '%s'. "
945cb93a386Sopenharmony_ci                     "GM tests will be skipped.\n", gpuConfig->getTag().c_str());
946cb93a386Sopenharmony_ci                return nullptr;
947cb93a386Sopenharmony_ci            }
948cb93a386Sopenharmony_ci            if (gpuConfig->getTestThreading()) {
949cb93a386Sopenharmony_ci                SkASSERT(!gpuConfig->getTestPersistentCache());
950cb93a386Sopenharmony_ci                return new GPUThreadTestingSink(gpuConfig, grCtxOptions);
951cb93a386Sopenharmony_ci            } else if (gpuConfig->getTestPersistentCache()) {
952cb93a386Sopenharmony_ci                return new GPUPersistentCacheTestingSink(gpuConfig, grCtxOptions);
953cb93a386Sopenharmony_ci            } else if (gpuConfig->getTestPrecompile()) {
954cb93a386Sopenharmony_ci                return new GPUPrecompileTestingSink(gpuConfig, grCtxOptions);
955cb93a386Sopenharmony_ci            } else if (gpuConfig->getUseDDLSink()) {
956cb93a386Sopenharmony_ci                return new GPUDDLSink(gpuConfig, grCtxOptions);
957cb93a386Sopenharmony_ci            } else if (gpuConfig->getOOPRish()) {
958cb93a386Sopenharmony_ci                return new GPUOOPRSink(gpuConfig, grCtxOptions);
959cb93a386Sopenharmony_ci            } else {
960cb93a386Sopenharmony_ci                return new GPUSink(gpuConfig, grCtxOptions);
961cb93a386Sopenharmony_ci            }
962cb93a386Sopenharmony_ci        }
963cb93a386Sopenharmony_ci    }
964cb93a386Sopenharmony_ci#ifdef SK_GRAPHITE_ENABLED
965cb93a386Sopenharmony_ci    if (FLAGS_graphite) {
966cb93a386Sopenharmony_ci        if (const SkCommandLineConfigGraphite *graphiteConfig = config->asConfigGraphite()) {
967cb93a386Sopenharmony_ci            return new GraphiteSink(graphiteConfig);
968cb93a386Sopenharmony_ci        }
969cb93a386Sopenharmony_ci    }
970cb93a386Sopenharmony_ci#endif
971cb93a386Sopenharmony_ci    if (const SkCommandLineConfigSvg* svgConfig = config->asConfigSvg()) {
972cb93a386Sopenharmony_ci        int pageIndex = svgConfig->getPageIndex();
973cb93a386Sopenharmony_ci        return new SVGSink(pageIndex);
974cb93a386Sopenharmony_ci    }
975cb93a386Sopenharmony_ci
976cb93a386Sopenharmony_ci#define SINK(t, sink, ...) if (config->getBackend().equals(t)) return new sink(__VA_ARGS__)
977cb93a386Sopenharmony_ci
978cb93a386Sopenharmony_ci    if (FLAGS_cpu) {
979cb93a386Sopenharmony_ci        SINK("g8",          RasterSink, kGray_8_SkColorType);
980cb93a386Sopenharmony_ci        SINK("565",         RasterSink, kRGB_565_SkColorType);
981cb93a386Sopenharmony_ci        SINK("4444",        RasterSink, kARGB_4444_SkColorType);
982cb93a386Sopenharmony_ci        SINK("8888",        RasterSink, kN32_SkColorType);
983cb93a386Sopenharmony_ci        SINK("rgba",        RasterSink, kRGBA_8888_SkColorType);
984cb93a386Sopenharmony_ci        SINK("bgra",        RasterSink, kBGRA_8888_SkColorType);
985cb93a386Sopenharmony_ci        SINK("rgbx",        RasterSink, kRGB_888x_SkColorType);
986cb93a386Sopenharmony_ci        SINK("1010102",     RasterSink, kRGBA_1010102_SkColorType);
987cb93a386Sopenharmony_ci        SINK("101010x",     RasterSink, kRGB_101010x_SkColorType);
988cb93a386Sopenharmony_ci        SINK("bgra1010102", RasterSink, kBGRA_1010102_SkColorType);
989cb93a386Sopenharmony_ci        SINK("bgr101010x",  RasterSink, kBGR_101010x_SkColorType);
990cb93a386Sopenharmony_ci        SINK("f16",         RasterSink, kRGBA_F16_SkColorType);
991cb93a386Sopenharmony_ci        SINK("f16norm",     RasterSink, kRGBA_F16Norm_SkColorType);
992cb93a386Sopenharmony_ci        SINK("f32",         RasterSink, kRGBA_F32_SkColorType);
993cb93a386Sopenharmony_ci        SINK("srgba",       RasterSink, kSRGBA_8888_SkColorType);
994cb93a386Sopenharmony_ci
995cb93a386Sopenharmony_ci        SINK("pdf",         PDFSink, false, SK_ScalarDefaultRasterDPI);
996cb93a386Sopenharmony_ci        SINK("skp",         SKPSink);
997cb93a386Sopenharmony_ci        SINK("svg",         SVGSink);
998cb93a386Sopenharmony_ci        SINK("null",        NullSink);
999cb93a386Sopenharmony_ci        SINK("xps",         XPSSink);
1000cb93a386Sopenharmony_ci        SINK("pdfa",        PDFSink, true,  SK_ScalarDefaultRasterDPI);
1001cb93a386Sopenharmony_ci        SINK("pdf300",      PDFSink, false, 300);
1002cb93a386Sopenharmony_ci        SINK("jsdebug",     DebugSink);
1003cb93a386Sopenharmony_ci    }
1004cb93a386Sopenharmony_ci#undef SINK
1005cb93a386Sopenharmony_ci    return nullptr;
1006cb93a386Sopenharmony_ci}
1007cb93a386Sopenharmony_ci
1008cb93a386Sopenharmony_cistatic Sink* create_via(const SkString& tag, Sink* wrapped) {
1009cb93a386Sopenharmony_ci#define VIA(t, via, ...) if (tag.equals(t)) return new via(__VA_ARGS__)
1010cb93a386Sopenharmony_ci#ifdef TEST_VIA_SVG
1011cb93a386Sopenharmony_ci    VIA("svg",       ViaSVG,               wrapped);
1012cb93a386Sopenharmony_ci#endif
1013cb93a386Sopenharmony_ci    VIA("serialize", ViaSerialization,     wrapped);
1014cb93a386Sopenharmony_ci    VIA("pic",       ViaPicture,           wrapped);
1015cb93a386Sopenharmony_ci    VIA("rtblend",   ViaRuntimeBlend,      wrapped);
1016cb93a386Sopenharmony_ci
1017cb93a386Sopenharmony_ci    if (FLAGS_matrix.count() == 4) {
1018cb93a386Sopenharmony_ci        SkMatrix m;
1019cb93a386Sopenharmony_ci        m.reset();
1020cb93a386Sopenharmony_ci        m.setScaleX((SkScalar)atof(FLAGS_matrix[0]));
1021cb93a386Sopenharmony_ci        m.setSkewX ((SkScalar)atof(FLAGS_matrix[1]));
1022cb93a386Sopenharmony_ci        m.setSkewY ((SkScalar)atof(FLAGS_matrix[2]));
1023cb93a386Sopenharmony_ci        m.setScaleY((SkScalar)atof(FLAGS_matrix[3]));
1024cb93a386Sopenharmony_ci        VIA("matrix",  ViaMatrix,  m, wrapped);
1025cb93a386Sopenharmony_ci        VIA("upright", ViaUpright, m, wrapped);
1026cb93a386Sopenharmony_ci    }
1027cb93a386Sopenharmony_ci
1028cb93a386Sopenharmony_ci#undef VIA
1029cb93a386Sopenharmony_ci
1030cb93a386Sopenharmony_ci    return nullptr;
1031cb93a386Sopenharmony_ci}
1032cb93a386Sopenharmony_ci
1033cb93a386Sopenharmony_cistatic bool gather_sinks(const GrContextOptions& grCtxOptions, bool defaultConfigs) {
1034cb93a386Sopenharmony_ci    SkCommandLineConfigArray configs;
1035cb93a386Sopenharmony_ci    ParseConfigs(FLAGS_config, &configs);
1036cb93a386Sopenharmony_ci    AutoreleasePool pool;
1037cb93a386Sopenharmony_ci    for (int i = 0; i < configs.count(); i++) {
1038cb93a386Sopenharmony_ci        const SkCommandLineConfig& config = *configs[i];
1039cb93a386Sopenharmony_ci        Sink* sink = create_sink(grCtxOptions, &config);
1040cb93a386Sopenharmony_ci        if (sink == nullptr) {
1041cb93a386Sopenharmony_ci            info("Skipping config %s: Don't understand '%s'.\n", config.getTag().c_str(),
1042cb93a386Sopenharmony_ci                 config.getTag().c_str());
1043cb93a386Sopenharmony_ci            continue;
1044cb93a386Sopenharmony_ci        }
1045cb93a386Sopenharmony_ci
1046cb93a386Sopenharmony_ci        // The command line config already parsed out the via-style color space. Apply it here.
1047cb93a386Sopenharmony_ci        sink->setColorSpace(config.refColorSpace());
1048cb93a386Sopenharmony_ci
1049cb93a386Sopenharmony_ci        const SkTArray<SkString>& parts = config.getViaParts();
1050cb93a386Sopenharmony_ci        for (int j = parts.count(); j-- > 0;) {
1051cb93a386Sopenharmony_ci            const SkString& part = parts[j];
1052cb93a386Sopenharmony_ci            Sink* next = create_via(part, sink);
1053cb93a386Sopenharmony_ci            if (next == nullptr) {
1054cb93a386Sopenharmony_ci                info("Skipping config %s: Don't understand '%s'.\n", config.getTag().c_str(),
1055cb93a386Sopenharmony_ci                     part.c_str());
1056cb93a386Sopenharmony_ci                delete sink;
1057cb93a386Sopenharmony_ci                sink = nullptr;
1058cb93a386Sopenharmony_ci                break;
1059cb93a386Sopenharmony_ci            }
1060cb93a386Sopenharmony_ci            sink = next;
1061cb93a386Sopenharmony_ci        }
1062cb93a386Sopenharmony_ci        if (sink) {
1063cb93a386Sopenharmony_ci            push_sink(config, sink);
1064cb93a386Sopenharmony_ci        }
1065cb93a386Sopenharmony_ci    }
1066cb93a386Sopenharmony_ci
1067cb93a386Sopenharmony_ci    // If no configs were requested (just running tests, perhaps?), then we're okay.
1068cb93a386Sopenharmony_ci    if (configs.count() == 0 ||
1069cb93a386Sopenharmony_ci        // If we're using the default configs, we're okay.
1070cb93a386Sopenharmony_ci        defaultConfigs ||
1071cb93a386Sopenharmony_ci        // Otherwise, make sure that all specified configs have become sinks.
1072cb93a386Sopenharmony_ci        configs.count() == gSinks->count()) {
1073cb93a386Sopenharmony_ci        return true;
1074cb93a386Sopenharmony_ci    }
1075cb93a386Sopenharmony_ci    return false;
1076cb93a386Sopenharmony_ci}
1077cb93a386Sopenharmony_ci
1078cb93a386Sopenharmony_cistatic bool match(const char* needle, const char* haystack) {
1079cb93a386Sopenharmony_ci    if ('~' == needle[0]) {
1080cb93a386Sopenharmony_ci        return !match(needle + 1, haystack);
1081cb93a386Sopenharmony_ci    }
1082cb93a386Sopenharmony_ci    if (0 == strcmp("_", needle)) {
1083cb93a386Sopenharmony_ci        return true;
1084cb93a386Sopenharmony_ci    }
1085cb93a386Sopenharmony_ci    return nullptr != strstr(haystack, needle);
1086cb93a386Sopenharmony_ci}
1087cb93a386Sopenharmony_ci
1088cb93a386Sopenharmony_cistatic bool should_skip(const char* sink, const char* src,
1089cb93a386Sopenharmony_ci                        const char* srcOptions, const char* name) {
1090cb93a386Sopenharmony_ci    for (int i = 0; i < FLAGS_skip.count() - 3; i += 4) {
1091cb93a386Sopenharmony_ci        if (match(FLAGS_skip[i+0], sink) &&
1092cb93a386Sopenharmony_ci            match(FLAGS_skip[i+1], src) &&
1093cb93a386Sopenharmony_ci            match(FLAGS_skip[i+2], srcOptions) &&
1094cb93a386Sopenharmony_ci            match(FLAGS_skip[i+3], name)) {
1095cb93a386Sopenharmony_ci            return true;
1096cb93a386Sopenharmony_ci        }
1097cb93a386Sopenharmony_ci    }
1098cb93a386Sopenharmony_ci    return false;
1099cb93a386Sopenharmony_ci}
1100cb93a386Sopenharmony_ci
1101cb93a386Sopenharmony_ci// Even when a Task Sink reports to be non-threadsafe (e.g. GPU), we know things like
1102cb93a386Sopenharmony_ci// .png encoding are definitely thread safe.  This lets us offload that work to CPU threads.
1103cb93a386Sopenharmony_cistatic SkTaskGroup* gDefinitelyThreadSafeWork = new SkTaskGroup;
1104cb93a386Sopenharmony_ci
1105cb93a386Sopenharmony_ci// The finest-grained unit of work we can run: draw a single Src into a single Sink,
1106cb93a386Sopenharmony_ci// report any errors, and perhaps write out the output: a .png of the bitmap, or a raw stream.
1107cb93a386Sopenharmony_cistruct Task {
1108cb93a386Sopenharmony_ci    Task(const TaggedSrc& src, const TaggedSink& sink) : src(src), sink(sink) {}
1109cb93a386Sopenharmony_ci    const TaggedSrc&  src;
1110cb93a386Sopenharmony_ci    const TaggedSink& sink;
1111cb93a386Sopenharmony_ci
1112cb93a386Sopenharmony_ci    static void Run(const Task& task) {
1113cb93a386Sopenharmony_ci        AutoreleasePool pool;
1114cb93a386Sopenharmony_ci        SkString name = task.src->name();
1115cb93a386Sopenharmony_ci
1116cb93a386Sopenharmony_ci        SkString log;
1117cb93a386Sopenharmony_ci        if (!FLAGS_dryRun) {
1118cb93a386Sopenharmony_ci            SkBitmap bitmap;
1119cb93a386Sopenharmony_ci            SkDynamicMemoryWStream stream;
1120cb93a386Sopenharmony_ci            start(task.sink.tag.c_str(), task.src.tag.c_str(),
1121cb93a386Sopenharmony_ci                  task.src.options.c_str(), name.c_str());
1122cb93a386Sopenharmony_ci            Result result = task.sink->draw(*task.src, &bitmap, &stream, &log);
1123cb93a386Sopenharmony_ci            if (!log.isEmpty()) {
1124cb93a386Sopenharmony_ci                info("%s %s %s %s:\n%s\n", task.sink.tag.c_str()
1125cb93a386Sopenharmony_ci                                         , task.src.tag.c_str()
1126cb93a386Sopenharmony_ci                                         , task.src.options.c_str()
1127cb93a386Sopenharmony_ci                                         , name.c_str()
1128cb93a386Sopenharmony_ci                                         , log.c_str());
1129cb93a386Sopenharmony_ci            }
1130cb93a386Sopenharmony_ci            if (result.isSkip()) {
1131cb93a386Sopenharmony_ci                done(task.sink.tag.c_str(), task.src.tag.c_str(),
1132cb93a386Sopenharmony_ci                     task.src.options.c_str(), name.c_str());
1133cb93a386Sopenharmony_ci                return;
1134cb93a386Sopenharmony_ci            }
1135cb93a386Sopenharmony_ci            if (result.isFatal()) {
1136cb93a386Sopenharmony_ci                fail(SkStringPrintf("%s %s %s %s: %s",
1137cb93a386Sopenharmony_ci                                    task.sink.tag.c_str(),
1138cb93a386Sopenharmony_ci                                    task.src.tag.c_str(),
1139cb93a386Sopenharmony_ci                                    task.src.options.c_str(),
1140cb93a386Sopenharmony_ci                                    name.c_str(),
1141cb93a386Sopenharmony_ci                                    result.c_str()));
1142cb93a386Sopenharmony_ci            }
1143cb93a386Sopenharmony_ci
1144cb93a386Sopenharmony_ci            // Run verifiers if specified
1145cb93a386Sopenharmony_ci            if (FLAGS_runVerifiers) {
1146cb93a386Sopenharmony_ci                RunGMVerifiers(task, bitmap);
1147cb93a386Sopenharmony_ci            }
1148cb93a386Sopenharmony_ci
1149cb93a386Sopenharmony_ci            // We're likely switching threads here, so we must capture by value, [=] or [foo,bar].
1150cb93a386Sopenharmony_ci            SkStreamAsset* data = stream.detachAsStream().release();
1151cb93a386Sopenharmony_ci            gDefinitelyThreadSafeWork->add([task,name,bitmap,data]{
1152cb93a386Sopenharmony_ci                std::unique_ptr<SkStreamAsset> ownedData(data);
1153cb93a386Sopenharmony_ci
1154cb93a386Sopenharmony_ci                std::unique_ptr<HashAndEncode> hashAndEncode;
1155cb93a386Sopenharmony_ci
1156cb93a386Sopenharmony_ci                SkString md5;
1157cb93a386Sopenharmony_ci                if (!FLAGS_writePath.isEmpty() || !FLAGS_readPath.isEmpty()) {
1158cb93a386Sopenharmony_ci                    SkMD5 hash;
1159cb93a386Sopenharmony_ci                    if (data->getLength()) {
1160cb93a386Sopenharmony_ci                        hash.writeStream(data, data->getLength());
1161cb93a386Sopenharmony_ci                        data->rewind();
1162cb93a386Sopenharmony_ci                    } else {
1163cb93a386Sopenharmony_ci                        hashAndEncode = std::make_unique<HashAndEncode>(bitmap);
1164cb93a386Sopenharmony_ci                        hashAndEncode->feedHash(&hash);
1165cb93a386Sopenharmony_ci                    }
1166cb93a386Sopenharmony_ci                    SkMD5::Digest digest = hash.finish();
1167cb93a386Sopenharmony_ci                    for (int i = 0; i < 16; i++) {
1168cb93a386Sopenharmony_ci                        md5.appendf("%02x", digest.data[i]);
1169cb93a386Sopenharmony_ci                    }
1170cb93a386Sopenharmony_ci                }
1171cb93a386Sopenharmony_ci
1172cb93a386Sopenharmony_ci                if (!FLAGS_readPath.isEmpty() &&
1173cb93a386Sopenharmony_ci                    !gGold->contains(Gold(task.sink.tag, task.src.tag,
1174cb93a386Sopenharmony_ci                                          task.src.options, name, md5))) {
1175cb93a386Sopenharmony_ci                    fail(SkStringPrintf("%s not found for %s %s %s %s in %s",
1176cb93a386Sopenharmony_ci                                        md5.c_str(),
1177cb93a386Sopenharmony_ci                                        task.sink.tag.c_str(),
1178cb93a386Sopenharmony_ci                                        task.src.tag.c_str(),
1179cb93a386Sopenharmony_ci                                        task.src.options.c_str(),
1180cb93a386Sopenharmony_ci                                        name.c_str(),
1181cb93a386Sopenharmony_ci                                        FLAGS_readPath[0]));
1182cb93a386Sopenharmony_ci                }
1183cb93a386Sopenharmony_ci
1184cb93a386Sopenharmony_ci                // Tests sometimes use a nullptr ext to indicate no image should be uploaded.
1185cb93a386Sopenharmony_ci                const char* ext = task.sink->fileExtension();
1186cb93a386Sopenharmony_ci                if (ext && !FLAGS_writePath.isEmpty()) {
1187cb93a386Sopenharmony_ci                #if defined(SK_BUILD_FOR_MAC)
1188cb93a386Sopenharmony_ci                    if (FLAGS_rasterize_pdf && SkString("pdf").equals(ext)) {
1189cb93a386Sopenharmony_ci                        SkASSERT(data->getLength() > 0);
1190cb93a386Sopenharmony_ci
1191cb93a386Sopenharmony_ci                        sk_sp<SkData> blob = SkData::MakeFromStream(data, data->getLength());
1192cb93a386Sopenharmony_ci
1193cb93a386Sopenharmony_ci                        SkUniqueCFRef<CGDataProviderRef> provider{
1194cb93a386Sopenharmony_ci                            CGDataProviderCreateWithData(nullptr,
1195cb93a386Sopenharmony_ci                                                         blob->data(),
1196cb93a386Sopenharmony_ci                                                         blob->size(),
1197cb93a386Sopenharmony_ci                                                         nullptr)};
1198cb93a386Sopenharmony_ci
1199cb93a386Sopenharmony_ci                        SkUniqueCFRef<CGPDFDocumentRef> pdf{
1200cb93a386Sopenharmony_ci                            CGPDFDocumentCreateWithProvider(provider.get())};
1201cb93a386Sopenharmony_ci
1202cb93a386Sopenharmony_ci                        CGPDFPageRef page = CGPDFDocumentGetPage(pdf.get(), 1);
1203cb93a386Sopenharmony_ci
1204cb93a386Sopenharmony_ci                        CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
1205cb93a386Sopenharmony_ci                        const int w = (int)CGRectGetWidth (bounds),
1206cb93a386Sopenharmony_ci                                  h = (int)CGRectGetHeight(bounds);
1207cb93a386Sopenharmony_ci
1208cb93a386Sopenharmony_ci                        SkBitmap rasterized;
1209cb93a386Sopenharmony_ci                        rasterized.allocPixels(SkImageInfo::Make(
1210cb93a386Sopenharmony_ci                            w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
1211cb93a386Sopenharmony_ci                        rasterized.eraseColor(SK_ColorWHITE);
1212cb93a386Sopenharmony_ci
1213cb93a386Sopenharmony_ci                        SkUniqueCFRef<CGColorSpaceRef> cs{CGColorSpaceCreateDeviceRGB()};
1214cb93a386Sopenharmony_ci                        CGBitmapInfo info = kCGBitmapByteOrder32Big |
1215cb93a386Sopenharmony_ci                                            (CGBitmapInfo)kCGImageAlphaPremultipliedLast;
1216cb93a386Sopenharmony_ci
1217cb93a386Sopenharmony_ci                        SkUniqueCFRef<CGContextRef> ctx{CGBitmapContextCreate(
1218cb93a386Sopenharmony_ci                            rasterized.getPixels(), w,h,8, rasterized.rowBytes(), cs.get(), info)};
1219cb93a386Sopenharmony_ci                        CGContextDrawPDFPage(ctx.get(), page);
1220cb93a386Sopenharmony_ci
1221cb93a386Sopenharmony_ci                        // Skip calling hashAndEncode->feedHash(SkMD5*)... we want the .pdf's hash.
1222cb93a386Sopenharmony_ci                        hashAndEncode = std::make_unique<HashAndEncode>(rasterized);
1223cb93a386Sopenharmony_ci                        WriteToDisk(task, md5, "png", nullptr,0, &rasterized, hashAndEncode.get());
1224cb93a386Sopenharmony_ci                    } else
1225cb93a386Sopenharmony_ci                #endif
1226cb93a386Sopenharmony_ci                    if (data->getLength()) {
1227cb93a386Sopenharmony_ci                        WriteToDisk(task, md5, ext, data, data->getLength(), nullptr, nullptr);
1228cb93a386Sopenharmony_ci                        SkASSERT(bitmap.drawsNothing());
1229cb93a386Sopenharmony_ci                    } else if (!bitmap.drawsNothing()) {
1230cb93a386Sopenharmony_ci                        WriteToDisk(task, md5, ext, nullptr, 0, &bitmap, hashAndEncode.get());
1231cb93a386Sopenharmony_ci                    }
1232cb93a386Sopenharmony_ci                }
1233cb93a386Sopenharmony_ci
1234cb93a386Sopenharmony_ci                SkPixmap pm;
1235cb93a386Sopenharmony_ci                if (FLAGS_checkF16 && bitmap.colorType() == kRGBA_F16Norm_SkColorType &&
1236cb93a386Sopenharmony_ci                        bitmap.peekPixels(&pm)) {
1237cb93a386Sopenharmony_ci                    bool unclamped = false;
1238cb93a386Sopenharmony_ci                    for (int y = 0; y < pm.height() && !unclamped; ++y)
1239cb93a386Sopenharmony_ci                    for (int x = 0; x < pm.width() && !unclamped; ++x) {
1240cb93a386Sopenharmony_ci                        Sk4f rgba = SkHalfToFloat_finite_ftz(*pm.addr64(x, y));
1241cb93a386Sopenharmony_ci                        float a = rgba[3];
1242cb93a386Sopenharmony_ci                        if (a > 1.0f || (rgba < 0.0f).anyTrue() || (rgba > a).anyTrue()) {
1243cb93a386Sopenharmony_ci                            SkDebugf("[%s] F16Norm pixel [%d, %d] unclamped: (%g, %g, %g, %g)\n",
1244cb93a386Sopenharmony_ci                                     name.c_str(), x, y, rgba[0], rgba[1], rgba[2], rgba[3]);
1245cb93a386Sopenharmony_ci                            unclamped = true;
1246cb93a386Sopenharmony_ci                        }
1247cb93a386Sopenharmony_ci                    }
1248cb93a386Sopenharmony_ci                }
1249cb93a386Sopenharmony_ci            });
1250cb93a386Sopenharmony_ci        }
1251cb93a386Sopenharmony_ci        done(task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str());
1252cb93a386Sopenharmony_ci    }
1253cb93a386Sopenharmony_ci
1254cb93a386Sopenharmony_ci    static SkString identify_gamut(SkColorSpace* cs) {
1255cb93a386Sopenharmony_ci        if (!cs) {
1256cb93a386Sopenharmony_ci            return SkString("untagged");
1257cb93a386Sopenharmony_ci        }
1258cb93a386Sopenharmony_ci
1259cb93a386Sopenharmony_ci        skcms_Matrix3x3 gamut;
1260cb93a386Sopenharmony_ci        if (cs->toXYZD50(&gamut)) {
1261cb93a386Sopenharmony_ci            auto eq = [](skcms_Matrix3x3 x, skcms_Matrix3x3 y) {
1262cb93a386Sopenharmony_ci                for (int i = 0; i < 3; i++)
1263cb93a386Sopenharmony_ci                for (int j = 0; j < 3; j++) {
1264cb93a386Sopenharmony_ci                    if (x.vals[i][j] != y.vals[i][j]) { return false; }
1265cb93a386Sopenharmony_ci                }
1266cb93a386Sopenharmony_ci                return true;
1267cb93a386Sopenharmony_ci            };
1268cb93a386Sopenharmony_ci
1269cb93a386Sopenharmony_ci            if (eq(gamut, SkNamedGamut::kSRGB     )) { return SkString("sRGB"); }
1270cb93a386Sopenharmony_ci            if (eq(gamut, SkNamedGamut::kAdobeRGB )) { return SkString("Adobe"); }
1271cb93a386Sopenharmony_ci            if (eq(gamut, SkNamedGamut::kDisplayP3)) { return SkString("P3"); }
1272cb93a386Sopenharmony_ci            if (eq(gamut, SkNamedGamut::kRec2020  )) { return SkString("2020"); }
1273cb93a386Sopenharmony_ci            if (eq(gamut, SkNamedGamut::kXYZ      )) { return SkString("XYZ"); }
1274cb93a386Sopenharmony_ci            if (eq(gamut,     gNarrow_toXYZD50    )) { return SkString("narrow"); }
1275cb93a386Sopenharmony_ci            return SkString("other");
1276cb93a386Sopenharmony_ci        }
1277cb93a386Sopenharmony_ci        return SkString("non-XYZ");
1278cb93a386Sopenharmony_ci    }
1279cb93a386Sopenharmony_ci
1280cb93a386Sopenharmony_ci    static SkString identify_transfer_fn(SkColorSpace* cs) {
1281cb93a386Sopenharmony_ci        if (!cs) {
1282cb93a386Sopenharmony_ci            return SkString("untagged");
1283cb93a386Sopenharmony_ci        }
1284cb93a386Sopenharmony_ci
1285cb93a386Sopenharmony_ci        auto eq = [](skcms_TransferFunction x, skcms_TransferFunction y) {
1286cb93a386Sopenharmony_ci            return x.g == y.g
1287cb93a386Sopenharmony_ci                && x.a == y.a
1288cb93a386Sopenharmony_ci                && x.b == y.b
1289cb93a386Sopenharmony_ci                && x.c == y.c
1290cb93a386Sopenharmony_ci                && x.d == y.d
1291cb93a386Sopenharmony_ci                && x.e == y.e
1292cb93a386Sopenharmony_ci                && x.f == y.f;
1293cb93a386Sopenharmony_ci        };
1294cb93a386Sopenharmony_ci
1295cb93a386Sopenharmony_ci        skcms_TransferFunction tf;
1296cb93a386Sopenharmony_ci        cs->transferFn(&tf);
1297cb93a386Sopenharmony_ci        switch (classify_transfer_fn(tf)) {
1298cb93a386Sopenharmony_ci            case sRGBish_TF:
1299cb93a386Sopenharmony_ci                if (tf.a == 1 && tf.b == 0 && tf.c == 0 && tf.d == 0 && tf.e == 0 && tf.f == 0) {
1300cb93a386Sopenharmony_ci                    return SkStringPrintf("gamma %.3g", tf.g);
1301cb93a386Sopenharmony_ci                }
1302cb93a386Sopenharmony_ci                if (eq(tf, SkNamedTransferFn::kSRGB)) { return SkString("sRGB"); }
1303cb93a386Sopenharmony_ci                if (eq(tf, SkNamedTransferFn::kRec2020)) { return SkString("2020"); }
1304cb93a386Sopenharmony_ci                return SkStringPrintf("%.3g %.3g %.3g %.3g %.3g %.3g %.3g",
1305cb93a386Sopenharmony_ci                                        tf.g, tf.a, tf.b, tf.c, tf.d, tf.e, tf.f);
1306cb93a386Sopenharmony_ci
1307cb93a386Sopenharmony_ci            case PQish_TF:
1308cb93a386Sopenharmony_ci                if (eq(tf, SkNamedTransferFn::kPQ)) { return SkString("PQ"); }
1309cb93a386Sopenharmony_ci                return SkStringPrintf("PQish %.3g %.3g %.3g %.3g %.3g %.3g",
1310cb93a386Sopenharmony_ci                                      tf.a, tf.b, tf.c, tf.d, tf.e, tf.f);
1311cb93a386Sopenharmony_ci
1312cb93a386Sopenharmony_ci            case HLGish_TF:
1313cb93a386Sopenharmony_ci                if (eq(tf, SkNamedTransferFn::kHLG)) { return SkString("HLG"); }
1314cb93a386Sopenharmony_ci                return SkStringPrintf("HLGish %.3g %.3g %.3g %.3g %.3g (%.3g)",
1315cb93a386Sopenharmony_ci                                      tf.a, tf.b, tf.c, tf.d, tf.e, tf.f+1);
1316cb93a386Sopenharmony_ci
1317cb93a386Sopenharmony_ci            case HLGinvish_TF: break;
1318cb93a386Sopenharmony_ci            case Bad_TF: break;
1319cb93a386Sopenharmony_ci        }
1320cb93a386Sopenharmony_ci        return SkString("non-numeric");
1321cb93a386Sopenharmony_ci    }
1322cb93a386Sopenharmony_ci
1323cb93a386Sopenharmony_ci    static void WriteToDisk(const Task& task,
1324cb93a386Sopenharmony_ci                            SkString md5,
1325cb93a386Sopenharmony_ci                            const char* ext,
1326cb93a386Sopenharmony_ci                            SkStream* data, size_t len,
1327cb93a386Sopenharmony_ci                            const SkBitmap* bitmap,
1328cb93a386Sopenharmony_ci                            const HashAndEncode* hashAndEncode) {
1329cb93a386Sopenharmony_ci
1330cb93a386Sopenharmony_ci        JsonWriter::BitmapResult result;
1331cb93a386Sopenharmony_ci        result.name          = task.src->name();
1332cb93a386Sopenharmony_ci        result.config        = task.sink.tag;
1333cb93a386Sopenharmony_ci        result.sourceType    = task.src.tag;
1334cb93a386Sopenharmony_ci        result.sourceOptions = task.src.options;
1335cb93a386Sopenharmony_ci        result.ext           = ext;
1336cb93a386Sopenharmony_ci        result.md5           = md5;
1337cb93a386Sopenharmony_ci        if (bitmap) {
1338cb93a386Sopenharmony_ci            result.gamut         = identify_gamut            (bitmap->colorSpace());
1339cb93a386Sopenharmony_ci            result.transferFn    = identify_transfer_fn      (bitmap->colorSpace());
1340cb93a386Sopenharmony_ci            result.colorType     = ToolUtils::colortype_name (bitmap->colorType());
1341cb93a386Sopenharmony_ci            result.alphaType     = ToolUtils::alphatype_name (bitmap->alphaType());
1342cb93a386Sopenharmony_ci            result.colorDepth    = ToolUtils::colortype_depth(bitmap->colorType());
1343cb93a386Sopenharmony_ci        }
1344cb93a386Sopenharmony_ci        JsonWriter::AddBitmapResult(result);
1345cb93a386Sopenharmony_ci
1346cb93a386Sopenharmony_ci        // If an MD5 is uninteresting, we want it noted in the JSON file,
1347cb93a386Sopenharmony_ci        // but don't want to dump it out as a .png (or whatever ext is).
1348cb93a386Sopenharmony_ci        if (gUninterestingHashes->contains(md5)) {
1349cb93a386Sopenharmony_ci            return;
1350cb93a386Sopenharmony_ci        }
1351cb93a386Sopenharmony_ci
1352cb93a386Sopenharmony_ci        const char* dir = FLAGS_writePath[0];
1353cb93a386Sopenharmony_ci        SkString resources = GetResourcePath();
1354cb93a386Sopenharmony_ci        if (0 == strcmp(dir, "@")) {  // Needed for iOS.
1355cb93a386Sopenharmony_ci            dir = resources.c_str();
1356cb93a386Sopenharmony_ci        }
1357cb93a386Sopenharmony_ci        sk_mkdir(dir);
1358cb93a386Sopenharmony_ci
1359cb93a386Sopenharmony_ci        SkString path;
1360cb93a386Sopenharmony_ci        if (FLAGS_nameByHash) {
1361cb93a386Sopenharmony_ci            path = SkOSPath::Join(dir, result.md5.c_str());
1362cb93a386Sopenharmony_ci            path.append(".");
1363cb93a386Sopenharmony_ci            path.append(ext);
1364cb93a386Sopenharmony_ci            if (sk_exists(path.c_str())) {
1365cb93a386Sopenharmony_ci                return;  // Content-addressed.  If it exists already, we're done.
1366cb93a386Sopenharmony_ci            }
1367cb93a386Sopenharmony_ci        } else {
1368cb93a386Sopenharmony_ci            path = SkOSPath::Join(dir, task.sink.tag.c_str());
1369cb93a386Sopenharmony_ci            sk_mkdir(path.c_str());
1370cb93a386Sopenharmony_ci            path = SkOSPath::Join(path.c_str(), task.src.tag.c_str());
1371cb93a386Sopenharmony_ci            sk_mkdir(path.c_str());
1372cb93a386Sopenharmony_ci            if (0 != strcmp(task.src.options.c_str(), "")) {
1373cb93a386Sopenharmony_ci              path = SkOSPath::Join(path.c_str(), task.src.options.c_str());
1374cb93a386Sopenharmony_ci              sk_mkdir(path.c_str());
1375cb93a386Sopenharmony_ci            }
1376cb93a386Sopenharmony_ci            path = SkOSPath::Join(path.c_str(), task.src->name().c_str());
1377cb93a386Sopenharmony_ci            path.append(".");
1378cb93a386Sopenharmony_ci            path.append(ext);
1379cb93a386Sopenharmony_ci        }
1380cb93a386Sopenharmony_ci
1381cb93a386Sopenharmony_ci        SkFILEWStream file(path.c_str());
1382cb93a386Sopenharmony_ci        if (!file.isValid()) {
1383cb93a386Sopenharmony_ci            fail(SkStringPrintf("Can't open %s for writing.\n", path.c_str()));
1384cb93a386Sopenharmony_ci            return;
1385cb93a386Sopenharmony_ci        }
1386cb93a386Sopenharmony_ci        if (bitmap) {
1387cb93a386Sopenharmony_ci            SkASSERT(hashAndEncode);
1388cb93a386Sopenharmony_ci            if (!hashAndEncode->encodePNG(&file,
1389cb93a386Sopenharmony_ci                                          result.md5.c_str(),
1390cb93a386Sopenharmony_ci                                          FLAGS_key,
1391cb93a386Sopenharmony_ci                                          FLAGS_properties)) {
1392cb93a386Sopenharmony_ci                fail(SkStringPrintf("Can't encode PNG to %s.\n", path.c_str()));
1393cb93a386Sopenharmony_ci                return;
1394cb93a386Sopenharmony_ci            }
1395cb93a386Sopenharmony_ci        } else {
1396cb93a386Sopenharmony_ci            if (!file.writeStream(data, len)) {
1397cb93a386Sopenharmony_ci                fail(SkStringPrintf("Can't write to %s.\n", path.c_str()));
1398cb93a386Sopenharmony_ci                return;
1399cb93a386Sopenharmony_ci            }
1400cb93a386Sopenharmony_ci        }
1401cb93a386Sopenharmony_ci    }
1402cb93a386Sopenharmony_ci
1403cb93a386Sopenharmony_ci    static void RunGMVerifiers(const Task& task, const SkBitmap& actualBitmap) {
1404cb93a386Sopenharmony_ci        const SkString name = task.src->name();
1405cb93a386Sopenharmony_ci        auto verifierList = task.src->getVerifiers();
1406cb93a386Sopenharmony_ci        if (verifierList == nullptr) {
1407cb93a386Sopenharmony_ci            return;
1408cb93a386Sopenharmony_ci        }
1409cb93a386Sopenharmony_ci
1410cb93a386Sopenharmony_ci        skiagm::verifiers::VerifierResult
1411cb93a386Sopenharmony_ci            res = verifierList->verifyAll(task.sink->colorInfo(), actualBitmap);
1412cb93a386Sopenharmony_ci        if (!res.ok()) {
1413cb93a386Sopenharmony_ci            fail(
1414cb93a386Sopenharmony_ci                SkStringPrintf(
1415cb93a386Sopenharmony_ci                    "%s %s %s %s: verifier failed: %s", task.sink.tag.c_str(), task.src.tag.c_str(),
1416cb93a386Sopenharmony_ci                    task.src.options.c_str(), name.c_str(), res.message().c_str()));
1417cb93a386Sopenharmony_ci        }
1418cb93a386Sopenharmony_ci    }
1419cb93a386Sopenharmony_ci};
1420cb93a386Sopenharmony_ci
1421cb93a386Sopenharmony_ci/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1422cb93a386Sopenharmony_ci
1423cb93a386Sopenharmony_ci// Unit tests don't fit so well into the Src/Sink model, so we give them special treatment.
1424cb93a386Sopenharmony_ci
1425cb93a386Sopenharmony_cistatic SkTDArray<skiatest::Test>* gParallelTests = new SkTDArray<skiatest::Test>;
1426cb93a386Sopenharmony_cistatic SkTDArray<skiatest::Test>* gSerialTests   = new SkTDArray<skiatest::Test>;
1427cb93a386Sopenharmony_ci
1428cb93a386Sopenharmony_cistatic void gather_tests() {
1429cb93a386Sopenharmony_ci    if (!FLAGS_src.contains("tests")) {
1430cb93a386Sopenharmony_ci        return;
1431cb93a386Sopenharmony_ci    }
1432cb93a386Sopenharmony_ci    for (const skiatest::Test& test : skiatest::TestRegistry::Range()) {
1433cb93a386Sopenharmony_ci        if (!in_shard()) {
1434cb93a386Sopenharmony_ci            continue;
1435cb93a386Sopenharmony_ci        }
1436cb93a386Sopenharmony_ci        if (CommandLineFlags::ShouldSkip(FLAGS_match, test.fName)) {
1437cb93a386Sopenharmony_ci            continue;
1438cb93a386Sopenharmony_ci        }
1439cb93a386Sopenharmony_ci        if (test.fNeedsGpu && FLAGS_gpu) {
1440cb93a386Sopenharmony_ci            gSerialTests->push_back(test);
1441cb93a386Sopenharmony_ci        } else if (test.fNeedsGraphite && FLAGS_graphite) {
1442cb93a386Sopenharmony_ci            gSerialTests->push_back(test);
1443cb93a386Sopenharmony_ci        } else if (!test.fNeedsGpu && !test.fNeedsGraphite && FLAGS_cpu) {
1444cb93a386Sopenharmony_ci            gParallelTests->push_back(test);
1445cb93a386Sopenharmony_ci        }
1446cb93a386Sopenharmony_ci    }
1447cb93a386Sopenharmony_ci}
1448cb93a386Sopenharmony_ci
1449cb93a386Sopenharmony_cistatic void run_test(skiatest::Test test, const GrContextOptions& grCtxOptions) {
1450cb93a386Sopenharmony_ci    struct : public skiatest::Reporter {
1451cb93a386Sopenharmony_ci        void reportFailed(const skiatest::Failure& failure) override {
1452cb93a386Sopenharmony_ci            fail(failure.toString());
1453cb93a386Sopenharmony_ci        }
1454cb93a386Sopenharmony_ci        bool allowExtendedTest() const override {
1455cb93a386Sopenharmony_ci            return FLAGS_pathOpsExtended;
1456cb93a386Sopenharmony_ci        }
1457cb93a386Sopenharmony_ci        bool verbose() const override { return FLAGS_veryVerbose; }
1458cb93a386Sopenharmony_ci    } reporter;
1459cb93a386Sopenharmony_ci
1460cb93a386Sopenharmony_ci    if (!FLAGS_dryRun && !should_skip("_", "tests", "_", test.fName)) {
1461cb93a386Sopenharmony_ci        AutoreleasePool pool;
1462cb93a386Sopenharmony_ci        GrContextOptions options = grCtxOptions;
1463cb93a386Sopenharmony_ci        test.modifyGrContextOptions(&options);
1464cb93a386Sopenharmony_ci
1465cb93a386Sopenharmony_ci        skiatest::ReporterContext ctx(&reporter, SkString(test.fName));
1466cb93a386Sopenharmony_ci        start("unit", "test", "", test.fName);
1467cb93a386Sopenharmony_ci        test.run(&reporter, options);
1468cb93a386Sopenharmony_ci    }
1469cb93a386Sopenharmony_ci    done("unit", "test", "", test.fName);
1470cb93a386Sopenharmony_ci}
1471cb93a386Sopenharmony_ci
1472cb93a386Sopenharmony_ci/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1473cb93a386Sopenharmony_ci
1474cb93a386Sopenharmony_ciint main(int argc, char** argv) {
1475cb93a386Sopenharmony_ci#if defined(__MSVC_RUNTIME_CHECKS)
1476cb93a386Sopenharmony_ci    _RTC_SetErrorFunc(RuntimeCheckErrorFunc);
1477cb93a386Sopenharmony_ci#endif
1478cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) && defined(SK_HAS_HEIF_LIBRARY)
1479cb93a386Sopenharmony_ci    android::ProcessState::self()->startThreadPool();
1480cb93a386Sopenharmony_ci#endif
1481cb93a386Sopenharmony_ci    CommandLineFlags::Parse(argc, argv);
1482cb93a386Sopenharmony_ci
1483cb93a386Sopenharmony_ci    initializeEventTracingForTools();
1484cb93a386Sopenharmony_ci
1485cb93a386Sopenharmony_ci#if !defined(SK_BUILD_FOR_GOOGLE3) && defined(SK_BUILD_FOR_IOS)
1486cb93a386Sopenharmony_ci    cd_Documents();
1487cb93a386Sopenharmony_ci#endif
1488cb93a386Sopenharmony_ci    setbuf(stdout, nullptr);
1489cb93a386Sopenharmony_ci    setup_crash_handler();
1490cb93a386Sopenharmony_ci
1491cb93a386Sopenharmony_ci    CommonFlags::SetDefaultFontMgr();
1492cb93a386Sopenharmony_ci    CommonFlags::SetAnalyticAA();
1493cb93a386Sopenharmony_ci
1494cb93a386Sopenharmony_ci    gSkForceRasterPipelineBlitter = FLAGS_forceRasterPipeline;
1495cb93a386Sopenharmony_ci    gUseSkVMBlitter               = FLAGS_skvm;
1496cb93a386Sopenharmony_ci    gSkVMAllowJIT                 = FLAGS_jit;
1497cb93a386Sopenharmony_ci
1498cb93a386Sopenharmony_ci    // The bots like having a verbose.log to upload, so always touch the file even if --verbose.
1499cb93a386Sopenharmony_ci    if (!FLAGS_writePath.isEmpty()) {
1500cb93a386Sopenharmony_ci        sk_mkdir(FLAGS_writePath[0]);
1501cb93a386Sopenharmony_ci        gVLog = fopen(SkOSPath::Join(FLAGS_writePath[0], "verbose.log").c_str(), "w");
1502cb93a386Sopenharmony_ci    }
1503cb93a386Sopenharmony_ci    if (FLAGS_verbose) {
1504cb93a386Sopenharmony_ci        gVLog = stderr;
1505cb93a386Sopenharmony_ci    }
1506cb93a386Sopenharmony_ci
1507cb93a386Sopenharmony_ci    GrContextOptions grCtxOptions;
1508cb93a386Sopenharmony_ci    CommonFlags::SetCtxOptions(&grCtxOptions);
1509cb93a386Sopenharmony_ci
1510cb93a386Sopenharmony_ci    dump_json();  // It's handy for the bots to assume this is ~never missing.
1511cb93a386Sopenharmony_ci
1512cb93a386Sopenharmony_ci    SkAutoGraphics ag;
1513cb93a386Sopenharmony_ci    SkTaskGroup::Enabler enabled(FLAGS_threads);
1514cb93a386Sopenharmony_ci
1515cb93a386Sopenharmony_ci    if (nullptr == GetResourceAsData("images/color_wheel.png")) {
1516cb93a386Sopenharmony_ci        info("Some resources are missing.  Do you need to set --resourcePath?\n");
1517cb93a386Sopenharmony_ci    }
1518cb93a386Sopenharmony_ci    gather_gold();
1519cb93a386Sopenharmony_ci    gather_uninteresting_hashes();
1520cb93a386Sopenharmony_ci
1521cb93a386Sopenharmony_ci    if (!gather_srcs()) {
1522cb93a386Sopenharmony_ci        return 1;
1523cb93a386Sopenharmony_ci    }
1524cb93a386Sopenharmony_ci    // TODO(dogben): This is a bit ugly. Find a cleaner way to do this.
1525cb93a386Sopenharmony_ci    bool defaultConfigs = true;
1526cb93a386Sopenharmony_ci    for (int i = 0; i < argc; i++) {
1527cb93a386Sopenharmony_ci        static constexpr char kConfigArg[] = "--config";
1528cb93a386Sopenharmony_ci        if (strcmp(argv[i], kConfigArg) == 0) {
1529cb93a386Sopenharmony_ci            defaultConfigs = false;
1530cb93a386Sopenharmony_ci            break;
1531cb93a386Sopenharmony_ci        }
1532cb93a386Sopenharmony_ci    }
1533cb93a386Sopenharmony_ci    if (!gather_sinks(grCtxOptions, defaultConfigs)) {
1534cb93a386Sopenharmony_ci        return 1;
1535cb93a386Sopenharmony_ci    }
1536cb93a386Sopenharmony_ci    gather_tests();
1537cb93a386Sopenharmony_ci    gPending = gSrcs->count() * gSinks->count() + gParallelTests->count() + gSerialTests->count();
1538cb93a386Sopenharmony_ci    info("%d srcs * %d sinks + %d tests == %d tasks\n",
1539cb93a386Sopenharmony_ci         gSrcs->count(), gSinks->count(), gParallelTests->count() + gSerialTests->count(),
1540cb93a386Sopenharmony_ci         gPending);
1541cb93a386Sopenharmony_ci
1542cb93a386Sopenharmony_ci    // Kick off as much parallel work as we can, making note of any serial work we'll need to do.
1543cb93a386Sopenharmony_ci    SkTaskGroup parallel;
1544cb93a386Sopenharmony_ci    SkTArray<Task> serial;
1545cb93a386Sopenharmony_ci
1546cb93a386Sopenharmony_ci    for (TaggedSink& sink : *gSinks) {
1547cb93a386Sopenharmony_ci        for (TaggedSrc& src : *gSrcs) {
1548cb93a386Sopenharmony_ci            if (src->veto(sink->flags()) ||
1549cb93a386Sopenharmony_ci                should_skip(sink.tag.c_str(), src.tag.c_str(),
1550cb93a386Sopenharmony_ci                            src.options.c_str(), src->name().c_str())) {
1551cb93a386Sopenharmony_ci                SkAutoSpinlock lock(*gMutex);
1552cb93a386Sopenharmony_ci                gPending--;
1553cb93a386Sopenharmony_ci                continue;
1554cb93a386Sopenharmony_ci            }
1555cb93a386Sopenharmony_ci
1556cb93a386Sopenharmony_ci            Task task(src, sink);
1557cb93a386Sopenharmony_ci            if (src->serial() || sink->serial()) {
1558cb93a386Sopenharmony_ci                serial.push_back(task);
1559cb93a386Sopenharmony_ci            } else {
1560cb93a386Sopenharmony_ci                parallel.add([task] { Task::Run(task); });
1561cb93a386Sopenharmony_ci            }
1562cb93a386Sopenharmony_ci        }
1563cb93a386Sopenharmony_ci    }
1564cb93a386Sopenharmony_ci    for (skiatest::Test& test : *gParallelTests) {
1565cb93a386Sopenharmony_ci        parallel.add([test, grCtxOptions] { run_test(test, grCtxOptions); });
1566cb93a386Sopenharmony_ci    }
1567cb93a386Sopenharmony_ci
1568cb93a386Sopenharmony_ci    // With the parallel work running, run serial tasks and tests here on main thread.
1569cb93a386Sopenharmony_ci    for (Task& task : serial) { Task::Run(task); }
1570cb93a386Sopenharmony_ci    for (skiatest::Test& test : *gSerialTests) { run_test(test, grCtxOptions); }
1571cb93a386Sopenharmony_ci
1572cb93a386Sopenharmony_ci    // Wait for any remaining parallel work to complete (including any spun off of serial tasks).
1573cb93a386Sopenharmony_ci    parallel.wait();
1574cb93a386Sopenharmony_ci    gDefinitelyThreadSafeWork->wait();
1575cb93a386Sopenharmony_ci
1576cb93a386Sopenharmony_ci    // At this point we're back in single-threaded land.
1577cb93a386Sopenharmony_ci
1578cb93a386Sopenharmony_ci    // We'd better have run everything.
1579cb93a386Sopenharmony_ci    SkASSERT(gPending == 0);
1580cb93a386Sopenharmony_ci    // Make sure we've flushed all our results to disk.
1581cb93a386Sopenharmony_ci    dump_json();
1582cb93a386Sopenharmony_ci
1583cb93a386Sopenharmony_ci    if (!gFailures->empty()) {
1584cb93a386Sopenharmony_ci        info("Failures:\n");
1585cb93a386Sopenharmony_ci        for (const SkString& fail : *gFailures) {
1586cb93a386Sopenharmony_ci            info("\t%s\n", fail.c_str());
1587cb93a386Sopenharmony_ci        }
1588cb93a386Sopenharmony_ci        info("%d failures\n", gFailures->count());
1589cb93a386Sopenharmony_ci        return 1;
1590cb93a386Sopenharmony_ci    }
1591cb93a386Sopenharmony_ci
1592cb93a386Sopenharmony_ci    SkGraphics::PurgeAllCaches();
1593cb93a386Sopenharmony_ci    info("Finished!\n");
1594cb93a386Sopenharmony_ci
1595cb93a386Sopenharmony_ci    return 0;
1596cb93a386Sopenharmony_ci}
1597