Skip to content

Screenshots and pixels#

The software renderer used by UI tests is deterministic, so what is drawn can be verified in a test. It is the last resort for custom drawing and styles; for layout and state, prefer Checking views.

Colors of a view#

averageColor(color, inaccuracy) takes a screenshot, clips it to the view and compares the average color. inaccuracy is in [0;1), where 0 demands the same color and 1 matches anything; the default is 0.1.

1
2
3
4
5
TEST_F(UIScreenAnalyzer, AverageColor) {
    // check the button is red
    By::text("Say hello").check(averageColor(0xff0000_rgb), "the button is not red");
    By::text("A basic button").check(uitest::impl::not$(averageColor(0xff0000_rgb)), "the button is red");
}

pixelColorAt({ x, y }, color, inaccuracy) checks one pixel. The position is relative to the view and within [0;1]: { 0, 0 } is the top-left corner, { 1, 1 } is the bottom-right, { 0.5f, 0.5f } is the center.

TEST_F(UIStyleTest, Merge) {
    testing::InSequence s;
    using namespace ass;

    mWindow->setExtraStylesheet(AStylesheet {
      {
        t<ASurface>(),
        BackgroundSolid { AColor::BLACK },
      },
      {
        ass::c("test1"),
        BackgroundImage { ":uni/caption/close.svg", {}, {}, Sizing::COVER }, // white cross
        FixedSize(32_px),
      },
      {
        ass::c("test2"),
        BackgroundImage { {}, AColor::RED },
      },
    });
    By::type<View>().check(pixelColorAt({0.5f, 0.5f}, AColor::BLACK)); // no white cross
    mView->addAssName("test1");
    By::type<View>().check(pixelColorAt({0.f, 0.5f}, AColor::BLACK)); // outside of cross
    By::type<View>().check(pixelColorAt({0.5f, 0.5f}, AColor::WHITE)); // within cross
    mView->addAssName("test2");
    By::type<View>().check(pixelColorAt({0.f, 0.5f}, AColor::BLACK)); // outside of cross
    By::type<View>().check(pixelColorAt({0.5f, 0.5f}, AColor::RED)); // within cross, overlayed by red
}

Pixel checks are the way to test ass rules: set an extra stylesheet, check the color.

Custom analysis#

ScreenshotAnalyzer gives access to the image. makeScreenshot() captures the window, clip(view) or clip(position, size) crops it, averageColor() and image() return the results.

auto shot = ScreenshotAnalyzer::makeScreenshot().clip(By::name("#preview").one());
EXPECT_EQ(shot.image().size(), glm::uvec2(64, 64));

Seeing what the test sees#

When a test fails, testing::UITest writes screenshots to the reports/ directory (relative to the working directory of the test run):

  • reports/<suite>/fail-<test>.png for every failed assertion, with the matched views outlined in red;
  • reports/<suite>/finish-<test>.png for the final state of every test.

To look at an intermediate state, call saveScreenshot("name") from the test body. The file is placed in reports/ and named <suite>.<test>_<name>.png.

TEST_F(UIDataBinding, Dropdown) {
    saveScreenshot("initial");
    By::type<ADropdownList>().perform(click());
    saveScreenshot("opened");
}

Tip

Upload reports/ as an artifact in your CI, so a red build comes with the pictures.