blob: bcf800d1de9ade7e21a25bb4b3cba8ce03304be7 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package graphics
import (
"context"
"fmt"
"image"
"image/color"
"net/http"
"net/http/httptest"
"os"
"path"
"path/filepath"
"time"
"chromiumos/tast/errors"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/graphics"
"chromiumos/tast/local/input"
"chromiumos/tast/local/screenshot"
"chromiumos/tast/testing"
"chromiumos/tast/testing/hwdep"
)
type params struct {
expectedID, resultID string
referenceColors []color.RGBA
}
const delayToScreenshot = 7 * time.Second
func init() {
testing.AddTest(&testing.Test{
Func: CompositorTransferFromImageBitmapOrientation,
LacrosStatus: testing.LacrosVariantUnknown,
Desc: "Verifies that transferFromImageBitmap is oriented correctly",
Contacts: []string{
"aswolfers@chromium.org",
"chromeos-gfx-compositor@google.com",
},
Attr: []string{"group:graphics", "graphics_nightly"},
Data: []string{"transfer-from-image-bitmap.html"},
SoftwareDeps: []string{"chrome"},
HardwareDeps: hwdep.D(hwdep.InternalDisplay()),
Fixture: "chromeLoggedIn",
Params: []testing.Param{{
Name: "2d",
Val: params{
expectedID: "expected2d",
resultID: "result2d",
referenceColors: []color.RGBA{
{255, 0, 0, 255},
{0, 0, 255, 255},
{0, 128, 0, 255},
{255, 255, 255, 255},
},
},
}, {
Name: "webgl",
Val: params{
expectedID: "expectedWebGL",
resultID: "resultWebGL",
referenceColors: []color.RGBA{
{255, 0, 0, 255},
{0, 0, 255, 255},
{0, 255, 0, 255},
{255, 255, 255, 255},
},
},
}},
})
}
// CompositorTransferFromImageBitmapOrientation renders to offscreen canvases, transfers their
// contents to bitmap renderers, and screenshots the results. The screenshots are compared against
// screenshots of reference canvases to validate that the results are oriented correctly.
func CompositorTransferFromImageBitmapOrientation(ctx context.Context, s *testing.State) {
server := httptest.NewServer(http.FileServer(s.DataFileSystem()))
defer server.Close()
cr := s.FixtValue().(*chrome.Chrome)
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Failed to connect to test API: ", err)
}
if err := graphics.RotateDisplayToLandscapePrimary(ctx, tconn); err != nil {
s.Fatal("Failed to set display to landscape-primary orientation: ", err)
}
url := path.Join(server.URL, "transfer-from-image-bitmap.html")
conn, err := cr.NewConn(ctx, url)
if err != nil {
s.Fatalf("Failed to open %v: %v", url, err)
}
defer conn.Close()
if err := conn.Call(ctx, nil, "executeDraw"); err != nil {
s.Fatal("Failed to execute drawing function")
}
kb, err := input.Keyboard(ctx)
if err != nil {
s.Fatal("Failed to initialize the keyboard writer: ", err)
}
defer kb.Close()
// screenshotCanvas requests a fullscreen view of the specified element, takes a screenshot,
// and returns the resulting image.
screenshotCanvas := func(id string) (image.Image, error) {
if err := conn.Call(ctx, nil, "setFullscreenEventListener", id); err != nil {
return nil, errors.Wrapf(err,
"failed to set fullscreen event listener on element %v", id)
}
if err := kb.Type(ctx, "f"); err != nil {
return nil, errors.Wrap(err, "failed to inject the 'f' key")
}
// Wait for the fullscreen transition to complete and for the escape message to
// disappear before taking the screenshot.
if err := testing.Sleep(ctx, delayToScreenshot); err != nil {
return nil, errors.Wrap(err, "failed to sleep prior to taking screenshot")
}
sshotPath := filepath.Join(s.OutDir(), fmt.Sprintf("%v.png", id))
if err := screenshot.Capture(ctx, sshotPath); err != nil {
return nil, errors.Wrapf(err, "failed to capture screenshot of element %v",
id)
}
f, err := os.Open(sshotPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to open %v", sshotPath)
}
defer f.Close()
img, _, err := image.Decode(f)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode %v", sshotPath)
}
return img, nil
}
params := s.Param().(params)
expectedImg, err := screenshotCanvas(params.expectedID)
if err != nil {
s.Fatalf("Failed to screenshot canvas %v: %v", params.expectedID, err)
}
resultImg, err := screenshotCanvas(params.resultID)
if err != nil {
s.Fatalf("Failed to screenshot canvas %v: %v", params.resultID, err)
}
if expectedImg.Bounds() != resultImg.Bounds() {
s.Fatalf("Mismatched image bounds: Expected %v, Actual %v", expectedImg.Bounds(),
resultImg.Bounds())
}
// Compare colors of pixels from within each image quadrant to verify orientation. Note that
// on non-square displays, the screenshots will have black margins. Correct for this by
// positioning each sample relative to the image center, offset by 1/4 of the smaller image
// dimension (which should generally be height in landscape mode).
width := expectedImg.Bounds().Dx()
height := expectedImg.Bounds().Dy()
cX := width / 2
cY := height / 2
offset := height / 4
if width < height {
offset = width / 4
}
samples := []image.Point{
{cX - offset, cY - offset},
{cX - offset, cY + offset},
{cX + offset, cY - offset},
{cX + offset, cY + offset},
}
for i, sample := range samples {
referenceColor := params.referenceColors[i]
expectedColor := expectedImg.At(sample.X, sample.Y)
if expectedColor != referenceColor {
s.Errorf("Expected image did not match reference color at (%d, %d): "+
"Expected %v, Actual %v",
sample.X, sample.Y, referenceColor, expectedColor)
continue
}
resultColor := resultImg.At(sample.X, sample.Y)
if expectedColor != resultColor {
s.Errorf("Mismatched colors at (%d, %d): Expected %v, Actual %v", sample.X,
sample.Y, expectedColor, resultColor)
}
}
}