blob: 0753055fd7f4bd8b0e54b5747d93918fbb368d64 [file] [log] [blame]
// Copyright 2019 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"
"io/ioutil"
"os"
"path/filepath"
"time"
"chromiumos/tast/common/testexec"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/display"
"chromiumos/tast/local/graphics"
"chromiumos/tast/local/input"
"chromiumos/tast/local/screenshot"
"chromiumos/tast/local/upstart"
"chromiumos/tast/testing"
"chromiumos/tast/testing/hwdep"
)
const (
smokePlatform = "platform"
smokeChrome = "chrome"
)
func init() {
testing.AddTest(&testing.Test{
Func: Smoke,
Desc: "Quick smoke check for GL/GLES2",
Contacts: []string{
"vsuley@chromium.org",
"hidehiko@chromium.org", // Tast port author
},
Attr: []string{"group:mainline"},
HardwareDeps: hwdep.D(hwdep.InternalDisplay()),
SoftwareDeps: []string{"no_qemu"},
Fixture: "gpuWatchHangs",
Params: []testing.Param{{
Name: "chrome",
ExtraSoftwareDeps: []string{"chrome"},
Val: smokeChrome,
}, {
Name: "platform",
ExtraData: []string{"screenshot1_reference.png", "screenshot2_reference.png"},
Val: smokePlatform,
}},
})
}
func Smoke(ctx context.Context, s *testing.State) {
number, err := graphics.NumberOfOutputsConnected(ctx)
if err != nil {
s.Fatal("Failed to get current connected monitors: ", err)
}
// TODO(pwang): Switch to use hardware dependency once it is ready.
if number <= 0 {
s.Fatal("Skipped as no monitor is detected")
}
// Explicitly switching to GUI. If the display is sleeping, this turns on it.
if err := switchToGUI(ctx); err != nil {
s.Fatal("Failed to switch to GUI: ", err)
}
testType := s.Param().(string)
if testType == smokeChrome {
testSomethingOnScreen(ctx, s)
}
if testType == smokePlatform {
testGeneratedScreenshot(ctx, s)
}
}
func switchToGUI(ctx context.Context) error {
kb, err := input.Keyboard(ctx)
if err != nil {
return err
}
defer kb.Close()
return kb.Accel(ctx, "Ctrl+Alt+F1")
}
// testSomethingOnScreen makes sure something is drawn on the screen, i.e. the display is
// not completely black.
func testSomethingOnScreen(ctx context.Context, s *testing.State) {
if err := upstart.RestartJob(ctx, "ui"); err != nil {
s.Fatal("Failed to restart ui job: ", err)
}
// Wait until screenshot can be taken.
if err := testing.Poll(ctx, func(ctx context.Context) error {
f, err := ioutil.TempFile("", "screenshot_test-*.png")
if err != nil {
return testing.PollBreak(err)
}
if err := f.Close(); err != nil {
return testing.PollBreak(err)
}
defer os.Remove(f.Name())
if err := screenshot.Capture(ctx, f.Name()); err != nil {
return err
}
return nil
}, &testing.PollOptions{Timeout: 10 * time.Second, Interval: time.Second}); err != nil {
s.Error("Screen didn't get ready: ", err)
return
}
signinPng := filepath.Join(s.OutDir(), "signin.png")
if err := screenshot.Capture(ctx, signinPng); err != nil {
s.Error("Failed to take screenshot on signin page: ", err)
return
}
cr, err := chrome.New(ctx)
if err != nil {
s.Error("Failed to log into Chrome: ", err)
return
}
defer cr.Close(ctx)
conn, err := cr.NewConn(ctx, "chrome://settings")
if err != nil {
s.Error("Failed to open chrome://settings: ", err)
return
}
defer conn.Close()
settingsPng := filepath.Join(s.OutDir(), "settings.png")
if err := screenshot.Capture(ctx, settingsPng); err != nil {
s.Error("Failed to take screenshot on settings page: ", err)
return
}
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Error("Failed to take TestAPI connection: ", err)
return
}
info, err := display.GetInfo(ctx, tconn)
if err != nil || len(info) < 1 {
s.Error("Failed to get display info: ", err)
return
}
w := int64(info[0].Bounds.Width)
h := int64(info[0].Bounds.Height)
// The threshold of the file size heuristically determined.
// Larger size means "some more information" on the screen. Smaller size
// means the screenshot is "empty" (i.e. close to solid color).
threshold := 15 * (w * h) / 1000000
for _, png := range []string{signinPng, settingsPng} {
if info, err := os.Stat(png); err != nil {
s.Errorf("Failed to stat %s: %v", filepath.Base(png), err)
} else if size := info.Size(); size < threshold {
// Screenshot filesize is smaller than expected. This indicates
// that there is nothing on screen. This ChromeOS image
// could be unusable.
s.Errorf("Screenshot file %s is too small: got %d, want >= %d", filepath.Base(png), size, threshold)
}
}
}
// testGeneratedScreenshot draws a texture with a soft ellipse twice and captures each image.
// Compares the output fuzzily against the reference images.
func testGeneratedScreenshot(ctx context.Context, s *testing.State) {
if err := upstart.StopJob(ctx, "ui"); err != nil {
s.Fatal("Failed to stop ui job: ", err)
}
defer upstart.StartJob(ctx, "ui")
tempdir, err := ioutil.TempDir("", "generated_screenshot")
if err != nil {
s.Error("Failed to create a tempdir: ", err)
return
}
defer os.RemoveAll(tempdir)
generated1 := filepath.Join(tempdir, "screenshot1_generated.png")
generated2 := filepath.Join(tempdir, "screenshot2_generated.png")
resized1 := filepath.Join(s.OutDir(), "screenshot1_resized.png")
resized2 := filepath.Join(s.OutDir(), "screenshot2_resized.png")
reference1 := s.DataPath("screenshot1_reference.png")
reference2 := s.DataPath("screenshot2_reference.png")
if err := testexec.CommandContext(ctx,
"/usr/local/glbench/bin/windowmanagertest",
// Delay before screenshot: 1 second has caused failures.
"--screenshot1_sec", "2",
"--screenshot2_sec", "1",
"--cooldown_sec", "1",
// perceptualdiff can handle only 8bit images.
"--screenshot1_cmd", "screenshot "+generated1,
"--screenshot2_cmd", "screenshot "+generated2,
).Run(testexec.DumpLogOnError); err != nil {
s.Error("Failed to run windowmanagertest: ", err)
return
}
resizePng := func(src, dst string, width, height int) error {
return testexec.CommandContext(ctx, "convert", "-channel", "RGB", "-colorspace", "RGB", "-depth", "8", "-resize", fmt.Sprintf("%dx%d!", width, height), src, dst).Run(testexec.DumpLogOnError)
}
if err := resizePng(generated1, resized1, 100, 100); err != nil {
s.Error("Failed to resize the screenshot 1: ", err)
return
}
if err := resizePng(generated2, resized2, 100, 100); err != nil {
s.Error("Failed to resize the screenshot 2: ", err)
return
}
if err := testexec.CommandContext(ctx, "perceptualdiff", "-verbose", reference1, resized1).Run(testexec.DumpLogOnError); err != nil {
s.Error("Unexpected diff from reference for screenshot 1: ", err)
}
if err := testexec.CommandContext(ctx, "perceptualdiff", "-verbose", reference2, resized2).Run(testexec.DumpLogOnError); err != nil {
s.Error("Unexpected diff from reference for screenshot 2: ", err)
}
}