blob: f2362663415488acb1872f929a42ffaa41a02715 [file] [log] [blame] [edit]
// 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 scanapp
import (
"context"
"path/filepath"
"time"
"chromiumos/tast/ctxutil"
"chromiumos/tast/local/bundles/cros/scanapp/scanning"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/ash"
"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: MultiPageScan,
Desc: "Tests that the Scan app can be used to perform multi-page flatbed PDF 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,
singlePagePdfGoldenFile,
twoPagePdfGoldenFile,
},
})
}
const (
singlePagePdfGoldenFile = "multi_page_flatbed_single_page.pdf"
twoPagePdfGoldenFile = "multi_page_flatbed_two_page.pdf"
)
var multiPageScanTests = []struct {
name string
removePage bool
rescanPage bool
goldenFile string
}{{
name: "multi_page_base",
removePage: false,
rescanPage: false,
goldenFile: twoPagePdfGoldenFile,
}, {
name: "multi_page_remove_page",
removePage: true,
rescanPage: false,
goldenFile: singlePagePdfGoldenFile,
}, {
name: "multi_page_rescan_page",
removePage: false,
rescanPage: true,
goldenFile: twoPagePdfGoldenFile,
},
}
func MultiPageScan(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()
crWithFeature, err := chrome.New(ctx, chrome.EnableFeatures("ScanAppMultiPageScan"))
if err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
defer crWithFeature.Close(cleanupCtx) // Close our own chrome instance
cr := crWithFeature
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 := ash.WaitForNotification(ctx, tconn, 30*time.Second, ash.WaitMessageContains(scanning.ScannerName)); err != nil {
s.Fatal("Failed to wait for printer notification: ", 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)
}
if err := uiauto.Combine("set scan settings",
app.SetScanSettings(scanapp.ScanSettings{
Scanner: scanning.ScannerName,
Source: scanapp.SourceFlatbed,
FileType: scanapp.FileTypePDF,
ColorMode: scanapp.ColorModeColor,
PageSize: scanapp.PageSizeLetter,
Resolution: scanapp.Resolution300DPI,
}),
app.ClickMultiPageScanCheckbox(),
)(ctx); err != nil {
s.Fatal("Failed to set scan settings: ", err)
}
for _, test := range multiPageScanTests {
s.Run(ctx, test.name, func(ctx context.Context, s *testing.State) {
defer faillog.DumpUITreeWithScreenshotOnError(cleanupCtx, s.OutDir(), s.HasError, cr, "ui_tree_multi_page_scan")
defer func() {
if err := scanning.RemoveScans(scanning.DefaultScanPattern); err != nil {
s.Error("Failed to remove scans: ", err)
}
}()
// Make sure printer connected notifications don't cover the Scan button.
if err := ash.CloseNotifications(ctx, tconn); err != nil {
s.Fatal("Failed to close notifications: ", err)
}
// Start a multi-page scan session and scan 2 pages.
if err := uiauto.Combine("multi-page scan",
app.MultiPageScan( /*PageNumber=*/ 1),
app.MultiPageScan( /*PageNumber=*/ 2),
)(ctx); err != nil {
s.Fatal("Failed to perform multi-page scan: ", err)
}
if test.removePage {
if err := app.RemovePage()(ctx); err != nil {
s.Fatal("Failed to remove page from scan: ", err)
}
}
if test.rescanPage {
if err := app.RescanPage()(ctx); err != nil {
s.Fatal("Failed to rescan page in scan: ", err)
}
}
// Click save to create the final PDF and compare it to the golden file.
if err := uiauto.Combine("save scan",
app.ClickSave(),
app.ClickDone(),
)(ctx); err != nil {
s.Fatal("Failed to save scan scan: ", err)
}
scan, err := scanning.GetScan(scanning.DefaultScanPattern)
if err != nil {
s.Fatal("Failed to find scan: ", err)
}
diffPath := filepath.Join(s.OutDir(), "multi_page_scan_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
}