예제 #1
0
int tool_main(int argc, char** argv) {
    DiffMetricProc diffProc = compute_diff_pmcolor;
    int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;

    // Maximum error tolerated in any one color channel in any one pixel before
    // a difference is reported.
    int colorThreshold = 0;
    SkString baseDir;
    SkString comparisonDir;
    SkString outputDir;

    StringArray matchSubstrings;
    StringArray nomatchSubstrings;

    bool generateDiffs = true;
    bool listFilenames = false;
    bool printDirNames = true;
    bool recurseIntoSubdirs = true;
    bool verbose = false;
    bool listFailingBase = false;

    RecordArray differences;
    DiffSummary summary;

    bool failOnResultType[DiffRecord::kResultCount];
    for (int i = 0; i < DiffRecord::kResultCount; i++) {
        failOnResultType[i] = false;
    }

    bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
    for (int base = 0; base < DiffResource::kStatusCount; ++base) {
        for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
            failOnStatusType[base][comparison] = false;
        }
    }

    int i;
    int numUnflaggedArguments = 0;
    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "--failonresult")) {
            if (argc == ++i) {
                SkDebugf("failonresult expects one argument.\n");
                continue;
            }
            DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
            if (type != DiffRecord::kResultCount) {
                failOnResultType[type] = true;
            } else {
                SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
            }
            continue;
        }
        if (!strcmp(argv[i], "--failonstatus")) {
            if (argc == ++i) {
                SkDebugf("failonstatus missing base status.\n");
                continue;
            }
            bool baseStatuses[DiffResource::kStatusCount];
            if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
                SkDebugf("unrecognized base status <%s>\n", argv[i]);
            }

            if (argc == ++i) {
                SkDebugf("failonstatus missing comparison status.\n");
                continue;
            }
            bool comparisonStatuses[DiffResource::kStatusCount];
            if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
                SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
            }

            for (int base = 0; base < DiffResource::kStatusCount; ++base) {
                for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
                    failOnStatusType[base][comparison] |=
                        baseStatuses[base] && comparisonStatuses[comparison];
                }
            }
            continue;
        }
        if (!strcmp(argv[i], "--help")) {
            usage(argv[0]);
            return kNoError;
        }
        if (!strcmp(argv[i], "--listfilenames")) {
            listFilenames = true;
            continue;
        }
        if (!strcmp(argv[i], "--verbose")) {
            verbose = true;
            continue;
        }
        if (!strcmp(argv[i], "--match")) {
            matchSubstrings.push(new SkString(argv[++i]));
            continue;
        }
        if (!strcmp(argv[i], "--nodiffs")) {
            generateDiffs = false;
            continue;
        }
        if (!strcmp(argv[i], "--nomatch")) {
            nomatchSubstrings.push(new SkString(argv[++i]));
            continue;
        }
        if (!strcmp(argv[i], "--noprintdirs")) {
            printDirNames = false;
            continue;
        }
        if (!strcmp(argv[i], "--norecurse")) {
            recurseIntoSubdirs = false;
            continue;
        }
        if (!strcmp(argv[i], "--sortbymaxmismatch")) {
            sortProc = compare<CompareDiffMaxMismatches>;
            continue;
        }
        if (!strcmp(argv[i], "--sortbymismatch")) {
            sortProc = compare<CompareDiffMeanMismatches>;
            continue;
        }
        if (!strcmp(argv[i], "--threshold")) {
            colorThreshold = atoi(argv[++i]);
            continue;
        }
        if (!strcmp(argv[i], "--weighted")) {
            sortProc = compare<CompareDiffWeighted>;
            continue;
        }
        if (argv[i][0] != '-') {
            switch (numUnflaggedArguments++) {
                case 0:
                    baseDir.set(argv[i]);
                    continue;
                case 1:
                    comparisonDir.set(argv[i]);
                    continue;
                case 2:
                    outputDir.set(argv[i]);
                    continue;
                default:
                    SkDebugf("extra unflagged argument <%s>\n", argv[i]);
                    usage(argv[0]);
                    return kGenericError;
            }
        }
        if (!strcmp(argv[i], "--listFailingBase")) {
            listFailingBase = true;
            continue;
        }

        SkDebugf("Unrecognized argument <%s>\n", argv[i]);
        usage(argv[0]);
        return kGenericError;
    }

    if (numUnflaggedArguments == 2) {
        outputDir = comparisonDir;
    } else if (numUnflaggedArguments != 3) {
        usage(argv[0]);
        return kGenericError;
    }

    if (!baseDir.endsWith(PATH_DIV_STR)) {
        baseDir.append(PATH_DIV_STR);
    }
    if (printDirNames) {
        printf("baseDir is [%s]\n", baseDir.c_str());
    }

    if (!comparisonDir.endsWith(PATH_DIV_STR)) {
        comparisonDir.append(PATH_DIV_STR);
    }
    if (printDirNames) {
        printf("comparisonDir is [%s]\n", comparisonDir.c_str());
    }

    if (!outputDir.endsWith(PATH_DIV_STR)) {
        outputDir.append(PATH_DIV_STR);
    }
    if (generateDiffs) {
        if (printDirNames) {
            printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
        }
    } else {
        if (printDirNames) {
            printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
        }
        outputDir.set("");
    }

    // If no matchSubstrings were specified, match ALL strings
    // (except for whatever nomatchSubstrings were specified, if any).
    if (matchSubstrings.isEmpty()) {
        matchSubstrings.push(new SkString(""));
    }

    create_diff_images(diffProc, colorThreshold, &differences,
                       baseDir, comparisonDir, outputDir,
                       matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
                       verbose, &summary);
    summary.print(listFilenames, failOnResultType, failOnStatusType);

    if (listFailingBase) {
        summary.printfFailingBaseNames("\n");
    }

    if (differences.count()) {
        qsort(differences.begin(), differences.count(),
              sizeof(DiffRecord*), sortProc);
    }

    if (generateDiffs) {
        print_diff_page(summary.fNumMatches, colorThreshold, differences,
                        baseDir, comparisonDir, outputDir);
    }

    for (i = 0; i < differences.count(); i++) {
        delete differences[i];
    }
    matchSubstrings.deleteAll();
    nomatchSubstrings.deleteAll();

    int num_failing_results = 0;
    for (int i = 0; i < DiffRecord::kResultCount; i++) {
        if (failOnResultType[i]) {
            num_failing_results += summary.fResultsOfType[i].count();
        }
    }
    if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
                if (failOnStatusType[base][comparison]) {
                    num_failing_results += summary.fStatusOfType[base][comparison].count();
                }
            }
        }
    }

    // On Linux (and maybe other platforms too), any results outside of the
    // range [0...255] are wrapped (mod 256).  Do the conversion ourselves, to
    // make sure that we only return 0 when there were no failures.
    return (num_failing_results > 255) ? 255 : num_failing_results;
}
예제 #2
0
int tool_main(int argc, char** argv) {
    DiffMetricProc diffProc = compute_diff_pmcolor;

    // Maximum error tolerated in any one color channel in any one pixel before
    // a difference is reported.
    int colorThreshold = 0;
    SkString baseFile;
    SkString baseLabel;
    SkString comparisonFile;
    SkString comparisonLabel;
    SkString outputDir;

    bool listFilenames = false;

    bool failOnResultType[DiffRecord::kResultCount];
    for (int i = 0; i < DiffRecord::kResultCount; i++) {
        failOnResultType[i] = false;
    }

    bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
    for (int base = 0; base < DiffResource::kStatusCount; ++base) {
        for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
            failOnStatusType[base][comparison] = false;
        }
    }

    int i;
    int numUnflaggedArguments = 0;
    int numLabelArguments = 0;
    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "--failonresult")) {
            if (argc == ++i) {
                SkDebugf("failonresult expects one argument.\n");
                continue;
            }
            DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
            if (type != DiffRecord::kResultCount) {
                failOnResultType[type] = true;
            } else {
                SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
            }
            continue;
        }
        if (!strcmp(argv[i], "--failonstatus")) {
            if (argc == ++i) {
                SkDebugf("failonstatus missing base status.\n");
                continue;
            }
            bool baseStatuses[DiffResource::kStatusCount];
            if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
                SkDebugf("unrecognized base status <%s>\n", argv[i]);
            }

            if (argc == ++i) {
                SkDebugf("failonstatus missing comparison status.\n");
                continue;
            }
            bool comparisonStatuses[DiffResource::kStatusCount];
            if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
                SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
            }

            for (int base = 0; base < DiffResource::kStatusCount; ++base) {
                for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
                    failOnStatusType[base][comparison] |=
                        baseStatuses[base] && comparisonStatuses[comparison];
                }
            }
            continue;
        }
        if (!strcmp(argv[i], "--help")) {
            usage(argv[0]);
            return kNoError;
        }
        if (!strcmp(argv[i], "--listfilenames")) {
            listFilenames = true;
            continue;
        }
        if (!strcmp(argv[i], "--outputdir")) {
            if (argc == ++i) {
                SkDebugf("outputdir expects one argument.\n");
                continue;
            }
            outputDir.set(argv[i]);
            continue;
        }
        if (!strcmp(argv[i], "--threshold")) {
            colorThreshold = atoi(argv[++i]);
            continue;
        }
        if (!strcmp(argv[i], "-u")) {
            //we don't produce unified diffs, ignore parameter to work with svn diff
            continue;
        }
        if (!strcmp(argv[i], "-L")) {
            if (argc == ++i) {
                SkDebugf("label expects one argument.\n");
                continue;
            }
            switch (numLabelArguments++) {
                case 0:
                    baseLabel.set(argv[i]);
                    continue;
                case 1:
                    comparisonLabel.set(argv[i]);
                    continue;
                default:
                    SkDebugf("extra label argument <%s>\n", argv[i]);
                    usage(argv[0]);
                    return kGenericError;
            }
            continue;
        }
        if (argv[i][0] != '-') {
            switch (numUnflaggedArguments++) {
                case 0:
                    baseFile.set(argv[i]);
                    continue;
                case 1:
                    comparisonFile.set(argv[i]);
                    continue;
                default:
                    SkDebugf("extra unflagged argument <%s>\n", argv[i]);
                    usage(argv[0]);
                    return kGenericError;
            }
        }

        SkDebugf("Unrecognized argument <%s>\n", argv[i]);
        usage(argv[0]);
        return kGenericError;
    }

    if (numUnflaggedArguments != 2) {
        usage(argv[0]);
        return kGenericError;
    }

    if (listFilenames) {
        printf("Base file is [%s]\n", baseFile.c_str());
    }

    if (listFilenames) {
        printf("Comparison file is [%s]\n", comparisonFile.c_str());
    }

    if (outputDir.isEmpty()) {
        if (listFilenames) {
            printf("Not writing any diffs. No output dir specified.\n");
        }
    } else {
        if (!outputDir.endsWith(PATH_DIV_STR)) {
            outputDir.append(PATH_DIV_STR);
        }
        if (listFilenames) {
            printf("Writing diffs. Output dir is [%s]\n", outputDir.c_str());
        }
    }

    // Some obscure documentation about diff/patch labels:
    //
    // Posix says the format is: <filename><tab><date>
    //     It also states that if a filename contains <tab> or <newline>
    //     the result is implementation defined
    //
    // Svn diff --diff-cmd provides labels of the form: <filename><tab><revision>
    //
    // Git diff --ext-diff does not supply arguments compatible with diff.
    //     However, it does provide the filename directly.
    //     skimagediff_git.sh: skimagediff %2 %5 -L "%1\t(%3)" -L "%1\t(%6)"
    //
    // Git difftool sets $LOCAL, $REMOTE, $MERGED, and $BASE instead of command line parameters.
    //     difftool.<>.cmd: skimagediff $LOCAL $REMOTE -L "$MERGED\t(local)" -L "$MERGED\t(remote)"
    //
    // Diff will write any specified label verbatim. Without a specified label diff will write
    //     <filename><tab><date>
    //     However, diff will encode the filename as a cstring if the filename contains
    //         Any of <space> or <double quote>
    //         A char less than 32
    //         Any escapable character \\, \a, \b, \t, \n, \v, \f, \r
    //
    // Patch decodes:
    //     If first <non-white-space> is <double quote>, parse filename from cstring.
    //     If there is a <tab> after the first <non-white-space>, filename is
    //         [first <non-white-space>, the next run of <white-space> with an embedded <tab>).
    //     Otherwise the filename is [first <non-space>, the next <white-space>).
    //
    // The filename /dev/null means the file does not exist (used in adds and deletes).

    // Considering the above, skimagediff will consider the contents of a -L parameter as
    //     <filename>(\t<specifier>)?
    SkString outputFile;

    if (baseLabel.isEmpty()) {
        baseLabel.set(baseFile);
        outputFile = baseLabel;
    } else {
        const char* baseLabelCstr = baseLabel.c_str();
        const char* tab = strchr(baseLabelCstr, '\t');
        if (nullptr == tab) {
            outputFile = baseLabel;
        } else {
            outputFile.set(baseLabelCstr, tab - baseLabelCstr);
        }
    }
    if (comparisonLabel.isEmpty()) {
        comparisonLabel.set(comparisonFile);
    }
    printf("Base:       %s\n", baseLabel.c_str());
    printf("Comparison: %s\n", comparisonLabel.c_str());

    DiffRecord dr;
    create_diff_images(diffProc, colorThreshold, baseFile, comparisonFile, outputDir, outputFile,
                       &dr);

    if (DiffResource::isStatusFailed(dr.fBase.fStatus)) {
        printf("Base %s.\n", DiffResource::getStatusDescription(dr.fBase.fStatus));
    }
    if (DiffResource::isStatusFailed(dr.fComparison.fStatus)) {
        printf("Comparison %s.\n", DiffResource::getStatusDescription(dr.fComparison.fStatus));
    }
    printf("Base and Comparison %s.\n", DiffRecord::getResultDescription(dr.fResult));

    if (DiffRecord::kDifferentPixels_Result == dr.fResult) {
        printf("%.4f%% of pixels differ", 100 * dr.fFractionDifference);
        printf(" (%.4f%%  weighted)", 100 * dr.fWeightedFraction);
        if (dr.fFractionDifference < 0.01) {
            printf(" %d pixels", static_cast<int>(dr.fFractionDifference *
                                                  dr.fBase.fBitmap.width() *
                                                  dr.fBase.fBitmap.height()));
        }

        printf("\nAverage color mismatch: ");
        printf("%d", static_cast<int>(MAX3(dr.fAverageMismatchR,
                                           dr.fAverageMismatchG,
                                           dr.fAverageMismatchB)));
        printf("\nMax color mismatch: ");
        printf("%d", MAX3(dr.fMaxMismatchR,
                          dr.fMaxMismatchG,
                          dr.fMaxMismatchB));
        printf("\n");
    }
    printf("\n");

    int num_failing_results = 0;
    if (failOnResultType[dr.fResult]) {
        ++num_failing_results;
    }
    if (failOnStatusType[dr.fBase.fStatus][dr.fComparison.fStatus]) {
        ++num_failing_results;
    }

    return num_failing_results;
}