Android API 10, 15, 16 emulators and any other Selendroid emulators, will intermittently take blank screenshots

Hi all,

I came across an issue with taking screenshots while running a test on an Android 4.1 API 16 emulator using Selendroid. After some trial and error I discovered that the Ruby screenshot method will intermittently (quite often) return an empty or garbage response, thus the screenshot that it takes is essentially empty/blank. I could not reproduce this issue on an Android 7.0 API 24 emulator (which uses UIAutomator vs Selendroid). Thus relying on the screenshot method does not seem feasible in its current state.

It appears that any Android emulator using Selendroid experiences this issue as I have confirmed Android API devices 10 - 16 also experience this issue.

I’ve attached the Appium server log and made it very easy to see one screenshot command being issued (which has a garbage value of “AA==” ) and then seeing the second screenshot command return an actual value string. To be clear, the first screenshot was blank, the second screenshot was an actual picture of what I was seeing on the emulator and expected for it to return.

I tried to add a sleep before the screenshot method and after the screenshot method to see if this would fix the problem, which it does not affect it at all. Then I tried to issue back-to-back screenshot statements, hoping the second one would always take an actual meaningful screenshot, but this was not the case either.

From what I concluded, over multiple hours of testing, is that no matter what you do the screenshot method will fail off and on (like 60-70% of the time), and by fail I mean it takes a blank screenshot.

A workaround that I thought might be great is to be able to issue the screenshot method command and then hook into the Appium server logs to see its returned response. The response could then be parsed by the “value” key and some logic could be performed around that so that I could determine if I should attempt to take another screenshot until it has passed a condition. However, I’m not sure how to do this or if this exists, and I’m not sure if this would work once I integrate my tests with SauceLabs. (If anyone knows how to do this then please share! Preferably in Ruby, but any language would help to provide an example on this.)

appium_server_log_for_android_api_16_intermittent_screenshot_failures.txt (19.0 KB)

Capabilities I’m using:

[caps]
platformName = "Android"
platformVersion = "4.1"
automationName = "Selendroid"
deviceName = "Android 4.1 Emulator"
app = "../../app-debug.apk"
avd = "Nexus_5X_API_16"
avdArgs = "-no-boot-anim"
rotatable = true
orientation = "PORTRAIT"

Ruby:

ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]

Appium:

appium 1.6.5

Appium Gem Versions:

appium_console (2.3.0)
appium_lib (9.4.9)

Update on Testing Efforts:
I looped the screenshot function to hammer out back-to-back screenshots of the main activity that is loaded when the app first starts (so as to keep the testing simple) and here were the results of taking 10 screenshots in a row:

Taking screenshot attempt 1 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_1.png’
Verifying it was not garbage…
Screenshot file size is: 75913 Byte(s)
Screenshot was valid!

Taking screenshot attempt 2 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_2.png’
Verifying it was not garbage…
Screenshot file size is: 1 Byte(s)
Screenshot was garbage!

Taking screenshot attempt 3 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_3.png’
Verifying it was not garbage…
Screenshot file size is: 1 Byte(s)
Screenshot was garbage!

Taking screenshot attempt 4 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_4.png’
Verifying it was not garbage…
Screenshot file size is: 75137 Byte(s)
Screenshot was valid!

Taking screenshot attempt 5 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_5.png’
Verifying it was not garbage…
Screenshot file size is: 1 Byte(s)
Screenshot was garbage!

Taking screenshot attempt 6 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_6.png’
Verifying it was not garbage…
Screenshot file size is: 75137 Byte(s)
Screenshot was valid!

Taking screenshot attempt 7 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_7.png’
Verifying it was not garbage…
Screenshot file size is: 1 Byte(s)
Screenshot was garbage!

Taking screenshot attempt 8 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_8.png’
Verifying it was not garbage…
Screenshot file size is: 1 Byte(s)
Screenshot was garbage!

Taking screenshot attempt 9 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_9.png’
Verifying it was not garbage…
Screenshot file size is: 75137 Byte(s)
Screenshot was valid!

Taking screenshot attempt 10 out of 10…
Saved screenshot: ‘tmp/screenshots_for_2017-07-19_15-51-09/android_4_1_phone/portrait_home_page_attempt_10.png’
Verifying it was not garbage…
Screenshot file size is: 1 Byte(s)
Screenshot was garbage!

Usually the first attempt will fail, and other attempts will succeed off and on, but this is a general idea of what is going on with this intermittent issue when calling the Ruby appium screenshot method.

The file that fails is always 1 Byte in size and its first bit is 00 when looking at it via hexdump (it doesn’t even have a header of “PNG” in it). So that’s why it feels like it’s an Appium issue.

One last thing, here is a method that I have written to help me get around this issue (though I don’t think it’s an ideal method if you need to take an action screenshot but it’s better than nothing for now):

def take_intelligent_screenshot(screenshot_path_and_name, max_retries=10)
  screenshot_attempts = 0
  loop do
    puts "Taking screenshot..."
    screenshot screenshot_path_and_name
    puts "Saved screenshot: '#{screenshot_path_and_name}'"
    screenshot_attempts += 1

    # Ensure the screenshot was not garbage
    puts "Verifying it was not garbage..."
    screenshot_file_size = File.size(screenshot_path_and_name)
    puts "Screenshot file size is: #{screenshot_file_size} Byte(s)"
    if screenshot_file_size <= 1
      puts "Screenshot was garbage! Retrying..."
      puts "Retry attempt #{screenshot_attempts} out #{max_retries}"
    else
      puts "Screenshot was valid!"
    end

    break if screenshot_file_size > 1 or screenshot_attempts == max_retries
  end
end