48 "Space-separated list of test cases (regexps) to run. Will run all tests if omitted.");
60 "Directory where to write any output .png and .json files. "
61 "Optional when running under Bazel "
62 "(e.g. \"bazel test //path/to:test\") as it defaults to "
63 "$TEST_UNDECLARED_OUTPUTS_DIR.");
67 "Plaintext file with one MD5 hash per line. This test runner will omit from "
68 "the output directory any images with an MD5 hash in this file.");
77 "Name of the Surface configuration to use (e.g. \"8888\"). This determines "
78 "how we construct the SkSurface from which we get the SkCanvas that GMs will "
79 "draw on. See file //tools/testrunners/common/surface_manager/SurfaceManager.h for "
85 "Contents of the \"cpu_or_gpu_value\" dimension for CPU-bound traces (e.g. \"AVX512\").");
90 "Contents of the \"cpu_or_gpu_value\" dimension for GPU-bound traces (e.g. \"RTX3060\").");
94 "Name of the \"via\" to use (e.g. \"picture_serialization\"). Optional.");
103 "Ignored by this test runner.",
116 std::map<std::string, std::string> commonKeys,
117 std::map<std::string, std::string> gmGoldKeys,
118 std::map<std::string, std::string> surfaceGoldKeys,
121 const char* jsonPath,
122 std::set<std::string> knownDigests) {
132 if (knownDigests.find(
md5.c_str()) != knownDigests.end()) {
135 .skippedDigest =
md5.c_str(),
148 .errorMsg =
"Error encoding or writing PNG to " + std::string(pngPath),
153 if (gmGoldKeys.find(
"name") == gmGoldKeys.end()) {
154 SK_ABORT(
"gmGoldKeys does not contain key \"name\"");
156 if (gmGoldKeys.find(
"source_type") == gmGoldKeys.end()) {
157 SK_ABORT(
"gmGoldKeys does not contain key \"source_type\"");
161 if (surfaceGoldKeys.find(
"surface_config") == surfaceGoldKeys.end()) {
162 SK_ABORT(
"surfaceGoldKeys does not contain key \"surface_config\"");
166 std::map<std::string, std::string> keys = {
167 {
"build_system",
"bazel"},
170 keys.merge(commonKeys);
171 keys.merge(surfaceGoldKeys);
172 keys.merge(gmGoldKeys);
180 for (
auto const& [param,
value] : keys) {
210void run_gm(std::unique_ptr<skiagm::GM> gm,
212 std::map<std::string, std::string> keyValuePairs,
215 std::string outputDir,
216 std::set<std::string> knownDigests) {
222 if (surfaceManager ==
nullptr) {
223 SK_ABORT(
"Unknown --surfaceConfig flag value: %s.", config.c_str());
230 "\tWarning: The surface is CPU-bound, but flag --cpuName was not provided. "
231 "Gold traces will omit keys \"cpu_or_gpu\" and \"cpu_or_gpu_value\".");
237 "\tWarning: The surface is GPU-bound, but flag --gpuName was not provided. "
238 "Gold traces will omit keys \"cpu_or_gpu\" and \"cpu_or_gpu_value\".");
251 std::string viaName = FLAGS_via.size() == 0 ?
"" : (FLAGS_via[0]);
253 output =
draw(gm.get(), surfaceManager->getSurface().get(), viaName);
265 surfaceManager->flush();
285 std::string
name = std::string(gm->getName().c_str());
293 surfaceManager->getGoldKeyValuePairs(cpuName, gpuName),
314 std::set<std::string> hashes;
315 std::regex md5HashRegex(
"^[a-fA-F0-9]{32}$");
316 std::ifstream f(path);
318 for (
int lineNum = 1; std::getline(f, line); lineNum++) {
320 auto isSpace = [](
unsigned char c) {
return !std::isspace(c); };
321 std::string
md5 = line;
322 md5.erase(
md5.begin(), std::find_if(
md5.begin(),
md5.end(), isSpace));
323 md5.erase(std::find_if(
md5.rbegin(),
md5.rend(), isSpace).base(),
md5.end());
325 if (
md5 ==
"")
continue;
327 if (!std::regex_match(
md5, md5HashRegex)) {
329 "File '%s' passed via --knownDigestsFile contains an invalid entry on line "
340int main(
int argc,
char** argv) {
347 std::string testUndeclaredOutputsDir;
348 if (
char* envVar = std::getenv(
"TEST_UNDECLARED_OUTPUTS_DIR")) {
349 testUndeclaredOutputsDir = envVar;
351 bool isBazelTest = !testUndeclaredOutputsDir.empty();
367 std::string outputDir =
368 FLAGS_outputDir.isEmpty() ? testUndeclaredOutputsDir : FLAGS_outputDir[0];
370 auto knownDigests = std::set<std::string>();
371 if (!FLAGS_knownDigestsFile.isEmpty()) {
374 "Read %zu known digests from: %s", knownDigests.size(), FLAGS_knownDigestsFile[0]);
377 std::map<std::string, std::string> keyValuePairs;
378 for (
int i = 1; i < FLAGS_key.size(); i += 2) {
379 keyValuePairs[FLAGS_key[i - 1]] = FLAGS_key[i];
381 std::string config = FLAGS_surfaceConfig[0];
382 std::string cpuName = FLAGS_cpuName.isEmpty() ?
"" : FLAGS_cpuName[0];
383 std::string gpuName = FLAGS_gpuName.isEmpty() ?
"" : FLAGS_gpuName[0];
387 std::string errorMsg = f();
388 if (errorMsg !=
"") {
389 SK_ABORT(
"Error while gathering GMs: %s", errorMsg.c_str());
393 std::unique_ptr<skiagm::GM> gm = f();
401 run_gm(std::move(gm), config, keyValuePairs, cpuName, gpuName, outputDir, knownDigests);
408 "%d successful GMs (images written to %s).",
gNumSuccessfulGMs, outputDir.c_str());
void run_gm(std::unique_ptr< skiagm::GM > gm, std::string config, std::map< std::string, std::string > keyValuePairs, std::string cpuName, std::string gpuName, std::string outputDir, std::set< std::string > knownDigests)
static int gNumSkippedGMs
static int gNumSuccessfulGMs
std::set< std::string > read_known_digests_file(std::string path)
static bool gMissingCpuOrGpuWarningLogged
static WritePNGAndJSONFilesResult write_png_and_json_files(std::string name, std::map< std::string, std::string > commonKeys, std::map< std::string, std::string > gmGoldKeys, std::map< std::string, std::string > surfaceGoldKeys, const SkBitmap &bitmap, const char *pngPath, const char *jsonPath, std::set< std::string > knownDigests)
static std::string draw_result_to_string(skiagm::DrawResult result)
static SkMD5::Digest md5(const SkBitmap &bm)
#define DEFINE_string(name, defaultValue, helpString)
std::map< std::string, std::string > GetCompilationModeGoldAndPerfKeyValuePairs()
static bool match(const char *needle, const char *haystack)
#define SK_ABORT(message,...)
static bool skip(SkStream *stream, size_t amount)
static uint32_t hash(const SkShaderBase::GradientInfo &v)
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
static void Parse(int argc, const char *const *argv)
bool encodePNG(SkWStream *, const char *md5, CommandLineFlags::StringArray key, CommandLineFlags::StringArray properties) const
void feedHash(SkWStream *) const
static bool CreateStringFlag(const char *name, const char *shortName, CommandLineFlags::StringArray *pStrings, const char *defaultValue, const char *helpString, const char *extendedHelpString)
void beginObject(const char *name=nullptr, bool multiline=true)
void appendString(const char *value, size_t size)
static SkString Join(const char *rootPath, const char *relativePath)
const char * c_str() const
static std::unique_ptr< SurfaceManager > FromConfig(std::string config, SurfaceOptions surfaceOptions)
void StringEven(std::string name, CommandLineFlags::StringArray flag)
void StringNonEmpty(std::string name, CommandLineFlags::StringArray flag)
void StringAtMostOne(std::string name, CommandLineFlags::StringArray flag)
bool ShouldRunTestCase(const char *name, CommandLineFlags::StringArray &matchFlag, CommandLineFlags::StringArray &skipFlag)
void Log(const char *format,...) SK_PRINTF_LIKE(1
void InitAndLogCmdlineArgs(int argc, char **argv)
std::function< std::unique_ptr< skiagm::GM >()> GMFactory
std::function< std::string()> GMRegistererFn
SkString toLowercaseHexString() const
enum WritePNGAndJSONFilesResult::@451 status
std::string skippedDigest