Wednesday, October 12, 2016

Python, Selenium and the dreaded "Timed out receiving message from renderer"

We had a problem, around 15% of the time, we were getting tests that failed, and when we went to SauceLabs to look at the problem, we just had a blank browser page with "data;" in the address field.

Folks here had been ignoring this and had just pronounced the automated tests to be flaky and unreliable. Having the experience I have, this was galling.

I looked in the selenium output, and saw "Timed out receiving message from renderer".

So I looked on Google, and found lots of folks reported having this problem. Several bugs have been written, all closed with "can't duplicate". This issue is at least 4 years old as of this writing. I tried changing timeouts, using try/except, and every other thing listed, but improvements were either nonexistent or very modest.

I have solved it, but the solution is an *awful* hack. The one thing it has going for it, our failures have dropped from 15% to 0. (Testing done using a suite with 10,000 cases in it.)

Here's the code at the center of the solution:

webdriver.get('about://blank')
my_script = 'var a = document.createElement("a");' \
            'var linkText = document.createTextNode("%s");' \
            'a.appendChild(linkText);' \
            'a.title = "%s";' \
            'a.href = "%s";' \
            'document.body.appendChild(a);' % \
            (url_to_use, url_to_use, url_to_use)
webdriver.execute_script(my_script)
webdriver.set_page_load_timeout(20)
webdriver.click_element_by_text('css=a', url_to_use)
if page.loaded() is False:
    webdriver.click_element_by_text('css=a', url_to_use)
if page.loaded() is False:
    webdriver.click_element_by_text('css=a', url_to_use)
if page.loaded() is False:
    webdriver.click_element_by_text('css=a', url_to_use)

In the above, page is my page object. The method loaded() checks controls to see if the page has finished loading. And  click_element_by_text fetches matching elements and iterates through them to determine whether they have the text specified. If an element does, it clicks it. (Sorry I can't include that code, but it belongs to work, and would make this sample way too long.)

In my experiments, it came to look like the integration between the driver and the browser (at least on Chrome) creates this state where Chrome has failed to load, but never tells the driver about it. So the driver just eventually times out.

about://blank - I used this because it should render an internally generated page every time. On Chrome, it's a "This site can't be reached" error, which works just fine. Firefox and IE also generate errors or blank pages. But the assumption was that internally stored pages should load every time. And so far, they do.

So by adding the target link and clicking it, I'm bypassing that tight integration.

I've watched the code run, and I've seen it had to retry once in a while, but so far, never more than once.

Please note this has only been tested on Chrome.

I hope somebody finds this helpful! :)

6 comments: