skia2/tools/skqp/make_gmkb.go
Hal Canary 2a7f0aa9eb SkQP: better error report workflow
* Generate report in app after running all tests.
  * Add script to unpack backup.ab file.
  * Remove unused index file.
  * Streamline instructions in README.md.

Change-Id: I44c80b17332eb4496ee31578287b691bd646d71a
Reviewed-on: https://skia-review.googlesource.com/86742
Commit-Queue: Hal Canary <halcanary@google.com>
Reviewed-by: Hal Canary <halcanary@google.com>
2017-12-20 19:23:46 +00:00

216 lines
5.0 KiB
Go

/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
package main
import (
"encoding/json"
"errors"
"fmt"
"image"
"image/draw"
"image/png"
"log"
"net/http"
"os"
"path"
"sort"
"strings"
"sync"
"go.skia.org/infra/golden/go/search"
)
const (
min_png = "min.png"
max_png = "max.png"
)
type ExportTestRecordArray []search.ExportTestRecord
func (a ExportTestRecordArray) Len() int { return len(a) }
func (a ExportTestRecordArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ExportTestRecordArray) Less(i, j int) bool { return a[i].TestName < a[j].TestName }
func in(v string, a []string) bool {
for _, u := range a {
if u == v {
return true
}
}
return false
}
// TODO(halcanary): clean up this blacklist.
var blacklist = []string{
"circular-clips",
"colorcomposefilter_wacky",
"coloremoji_blendmodes",
"colormatrix",
"complexclip_bw",
"complexclip_bw_invert",
"complexclip_bw_layer",
"complexclip_bw_layer_invert",
"convex-lineonly-paths-stroke-and-fill",
"dftext",
"downsamplebitmap_image_high_mandrill_512.png",
"downsamplebitmap_image_medium_mandrill_512.png",
"filterbitmap_image_mandrill_16.png",
"filterbitmap_image_mandrill_64.png",
"filterbitmap_image_mandrill_64.png_g8",
"gradients_degenerate_2pt",
"gradients_degenerate_2pt_nodither",
"gradients_local_perspective",
"gradients_local_perspective_nodither",
"imagefilterstransformed",
"image_scale_aligned",
"lattice",
"linear_gradient",
"mipmap_srgb",
"mixedtextblobs",
"OverStroke",
"simple-offsetimagefilter",
"strokerect",
"textblobmixedsizes",
"textblobmixedsizes_df"}
func processTest(testName string, imgUrls []string, output string) error {
if strings.ContainsRune(testName, '/') {
return nil
}
output_directory := path.Join(output, testName)
var img_max image.NRGBA
var img_min image.NRGBA
for _, url := range imgUrls {
resp, err := http.Get(url)
if err != nil {
return err
}
img, err := png.Decode(resp.Body)
resp.Body.Close()
if err != nil {
return err
}
if img_max.Rect.Max.X == 0 {
// N.B. img_max.Pix may alias img.Pix (if they're already NRGBA).
img_max = toNrgba(img)
img_min = copyNrgba(img_max)
continue
}
w := img.Bounds().Max.X - img.Bounds().Min.X
h := img.Bounds().Max.Y - img.Bounds().Min.Y
if img_max.Rect.Max.X != w || img_max.Rect.Max.Y != h {
return errors.New("size mismatch")
}
img_nrgba := toNrgba(img)
for i, value := range img_nrgba.Pix {
if value > img_max.Pix[i] {
img_max.Pix[i] = value
} else if value < img_min.Pix[i] {
img_min.Pix[i] = value
}
}
}
if img_max.Rect.Max.X == 0 {
return nil
}
if err := os.Mkdir(output_directory, os.ModePerm); err != nil && !os.IsExist(err) {
return err
}
if err := writePngToFile(path.Join(output_directory, min_png), &img_min); err != nil {
return err
}
if err := writePngToFile(path.Join(output_directory, max_png), &img_max); err != nil {
return err
}
return nil
}
func readMetaJsonFile(filename string) ([]search.ExportTestRecord, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
dec := json.NewDecoder(file)
var records []search.ExportTestRecord
err = dec.Decode(&records)
return records, err
}
func writePngToFile(path string, img image.Image) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
return png.Encode(file, img)
}
// to_nrgb() may return a shallow copy of img if it's already NRGBA.
func toNrgba(img image.Image) image.NRGBA {
switch v := img.(type) {
case *image.NRGBA:
return *v
}
nimg := *image.NewNRGBA(img.Bounds())
draw.Draw(&nimg, img.Bounds(), img, image.Point{0, 0}, draw.Src)
return nimg
}
func copyNrgba(src image.NRGBA) image.NRGBA {
dst := image.NRGBA{make([]uint8, len(src.Pix)), src.Stride, src.Rect}
copy(dst.Pix, src.Pix)
return dst
}
func main() {
if len(os.Args) != 3 {
log.Printf("Usage:\n %s INPUT.json OUTPUT_DIRECTORY\n\n", os.Args[0])
os.Exit(1)
}
input := os.Args[1]
output := os.Args[2]
// output is removed and replaced with a clean directory.
if err := os.RemoveAll(output); err != nil && !os.IsNotExist(err) {
log.Fatal(err)
}
if err := os.MkdirAll(output, os.ModePerm); err != nil && !os.IsExist(err) {
log.Fatal(err)
}
records, err := readMetaJsonFile(input)
if err != nil {
log.Fatal(err)
}
sort.Sort(ExportTestRecordArray(records))
var wg sync.WaitGroup
for _, record := range records {
if in(record.TestName, blacklist) {
continue
}
var goodUrls []string
for _, digest := range record.Digests {
if (in("vk", digest.ParamSet["config"]) ||
in("gles", digest.ParamSet["config"])) &&
digest.Status == "positive" {
goodUrls = append(goodUrls, digest.URL)
}
}
wg.Add(1)
go func(testName string, imgUrls []string, output string) {
defer wg.Done()
if err := processTest(testName, imgUrls, output); err != nil {
log.Fatal(err)
}
fmt.Printf("\r%-60s", testName)
}(record.TestName, goodUrls, output)
}
wg.Wait()
fmt.Printf("\r%60s\n", "")
}