blob: bab0004abd1f7e1d5d6f0f3b8d9dac5ddd7dfbaf [file] [log] [blame]
// Copyright 2020 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 scanapp
import (
"context"
"path/filepath"
"time"
"chromiumos/tast/ctxutil"
"chromiumos/tast/local/bundles/cros/scanapp/scanning"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/uiauto"
"chromiumos/tast/local/chrome/uiauto/faillog"
"chromiumos/tast/local/chrome/uiauto/scanapp"
"chromiumos/tast/local/printing/cups"
"chromiumos/tast/local/printing/document"
"chromiumos/tast/local/printing/ippusbbridge"
"chromiumos/tast/local/printing/usbprinter"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: Scan,
Desc: "Tests that the Scan app can be used to perform scans",
Contacts: []string{
"cros-peripherals@google.com",
"project-bolton@google.com",
},
Attr: []string{
"group:mainline",
"informational",
"group:paper-io",
"paper-io_scanning",
},
SoftwareDeps: []string{"chrome", "virtual_usb_printer"},
Data: []string{
scanning.SourceImage,
pngGoldenFile,
jpgGoldenFile,
pdfGoldenFile,
},
})
}
const (
pngGoldenFile = "flatbed_png_color_letter_300_dpi.png"
jpgGoldenFile = "adf_simplex_jpg_grayscale_a4_150_dpi.jpg"
pdfGoldenFile = "adf_duplex_pdf_grayscale_max_300_dpi.pdf"
)
var tests = []struct {
name string
settings scanapp.ScanSettings
goldenFile string
}{{
name: "flatbed_png_color_letter_300_dpi",
settings: scanapp.ScanSettings{
Scanner: scanning.ScannerName,
Source: scanapp.SourceFlatbed,
FileType: scanapp.FileTypePNG,
ColorMode: scanapp.ColorModeColor,
PageSize: scanapp.PageSizeLetter,
Resolution: scanapp.Resolution300DPI,
},
goldenFile: pngGoldenFile,
}, {
name: "adf_simplex_jpg_grayscale_a4_150_dpi",
settings: scanapp.ScanSettings{
Scanner: scanning.ScannerName,
Source: scanapp.SourceADFOneSided,
FileType: scanapp.FileTypeJPG,
// TODO(b/181773386): Change this to black and white when the virtual
// USB printer correctly reports the color mode.
ColorMode: scanapp.ColorModeGrayscale,
PageSize: scanapp.PageSizeA4,
Resolution: scanapp.Resolution150DPI,
},
goldenFile: jpgGoldenFile,
}, {
name: "adf_duplex_pdf_grayscale_max_300_dpi",
settings: scanapp.ScanSettings{
Scanner: scanning.ScannerName,
Source: scanapp.SourceADFTwoSided,
FileType: scanapp.FileTypePDF,
ColorMode: scanapp.ColorModeGrayscale,
PageSize: scanapp.PageSizeFitToScanArea,
Resolution: scanapp.Resolution300DPI,
},
goldenFile: pdfGoldenFile,
}}
func Scan(ctx context.Context, s *testing.State) {
// Use cleanupCtx for any deferred cleanups in case of timeouts or
// cancellations on the shortened context.
cleanupCtx := ctx
ctx, cancel := ctxutil.Shorten(ctx, 5*time.Second)
defer cancel()
cr, err := chrome.New(ctx)
if err != nil {
s.Fatal("Failed to create Chrome instance: ", err)
}
defer cr.Close(cleanupCtx)
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Failed to connect Test API: ", err)
}
defer faillog.DumpUITreeOnError(cleanupCtx, s.OutDir(), s.HasError, tconn)
// Set up the virtual USB printer.
if err := usbprinter.InstallModules(ctx); err != nil {
s.Fatal("Failed to install kernel modules: ", err)
}
defer func(ctx context.Context) {
if err := usbprinter.RemoveModules(ctx); err != nil {
s.Error("Failed to remove kernel modules: ", err)
}
}(cleanupCtx)
devInfo, err := usbprinter.LoadPrinterIDs(scanning.Descriptors)
if err != nil {
s.Fatalf("Failed to load printer IDs from %v: %v", scanning.Descriptors, err)
}
printer, err := usbprinter.StartScanner(ctx, devInfo, scanning.Descriptors, scanning.Attributes, scanning.EsclCapabilities, s.DataPath(scanning.SourceImage), "")
if err != nil {
s.Fatal("Failed to attach virtual printer: ", err)
}
defer func() {
if printer != nil {
usbprinter.StopPrinter(cleanupCtx, printer, devInfo)
}
}()
if err = ippusbbridge.WaitForSocket(ctx, devInfo); err != nil {
s.Fatal("Failed to wait for ippusb_bridge socket: ", err)
}
if err = cups.EnsurePrinterIdle(ctx, devInfo); err != nil {
s.Fatal("Failed to wait for printer to be idle: ", err)
}
if err = ippusbbridge.ContactPrinterEndpoint(ctx, devInfo, "/eSCL/ScannerCapabilities"); err != nil {
s.Fatal("Failed to get scanner status over ippusb_bridge socket: ", err)
}
// Launch the Scan app, configure the settings, and perform scans.
app, err := scanapp.Launch(ctx, tconn)
if err != nil {
s.Fatal("Failed to launch app: ", err)
}
if err := app.ClickMoreSettings()(ctx); err != nil {
s.Fatal("Failed to expand More settings: ", err)
}
for _, test := range tests {
s.Run(ctx, test.name, func(ctx context.Context, s *testing.State) {
defer faillog.DumpUITreeWithScreenshotOnError(cleanupCtx, s.OutDir(), s.HasError, cr, "ui_tree_"+test.name)
defer func() {
if err := scanning.RemoveScans(scanning.DefaultScanPattern); err != nil {
s.Error("Failed to remove scans: ", err)
}
}()
if err := uiauto.Combine("scan",
app.SetScanSettings(test.settings),
app.Scan(),
app.ClickDone(),
)(ctx); err != nil {
s.Fatal("Failed to perform scan: ", err)
}
scan, err := scanning.GetScan(scanning.DefaultScanPattern)
if err != nil {
s.Fatal("Failed to find scan: ", err)
}
diffPath := filepath.Join(s.OutDir(), test.name+"_diff.txt")
if err := document.CompareFiles(ctx, scan, s.DataPath(test.goldenFile), diffPath); err != nil {
s.Error("Scan differs from golden file: ", err)
}
})
}
// Intentionally stop the printer early to trigger shutdown in
// ippusb_bridge. Without this, cleanup may have to wait for other processes
// to finish using the printer (e.g. CUPS background probing).
usbprinter.StopPrinter(cleanupCtx, printer, devInfo)
printer = nil
}