#!/usr/bin/python # Copyright (c) 2011 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import copy import ctypes from distutils import version import fnmatch import glob import hashlib import logging import os import platform import shutil import subprocess import sys import tarfile import tempfile import urllib2 import xml.dom.minidom import pyauto_functional # Must be imported before pyauto. import pyauto import pyauto_utils class NaClSDKTest(pyauto.PyUITest): """Tests for the NaCl SDK.""" _extracted_sdk_path = None _temp_dir = None _settings = { 'release_win_sdk_url': 'http://commondatastorage.googleapis.com/' 'nativeclient-mirror/nacl/nacl_sdk/staging/naclsdk_win.exe', 'release_mac_sdk_url': 'http://commondatastorage.googleapis.com/' 'nativeclient-mirror/nacl/nacl_sdk/staging/naclsdk_mac.tgz', 'release_lin_sdk_url': 'http://commondatastorage.googleapis.com/' 'nativeclient-mirror/nacl/nacl_sdk/staging/naclsdk_linux.tgz', 'expected_md5_url': 'http://commondatastorage.googleapis.com/' 'nativeclient-mirror', 'release_win_expected_md5_key': 'nacl/nacl_sdk/staging/naclsdk_win.exe', 'release_mac_expected_md5_key': 'nacl/nacl_sdk/staging/naclsdk_mac.tgz', 'release_lin_expected_md5_key': 'nacl/nacl_sdk/staging/' 'naclsdk_linux.tgz', 'post_sdk_download_url': 'http://code.google.com/chrome/nativeclient/' 'docs/download.html', 'post_win_sdk_url': 'http://commondatastorage.googleapis.com/' 'nativeclient-mirror/nacl/nacl_sdk/naclsdk_win.exe', 'post_mac_sdk_url': 'http://commondatastorage.googleapis.com/' 'nativeclient-mirror/nacl/nacl_sdk/naclsdk_mac.tgz', 'post_lin_sdk_url': 'http://commondatastorage.googleapis.com/' 'nativeclient-mirror/nacl/nacl_sdk/naclsdk_linux.tgz', 'min_required_chrome_build': 14, 'gallery_examples': { 'life': 'http://nacl-gallery.appspot.com/life/life.html', 'hello_world': 'http://nacl-gallery.appspot.com/hello_world/' 'hello_world.html', 'pi_generator': 'http://nacl-gallery.appspot.com/pi_generator/' 'pi_generator.html', 'sine_synth': 'http://nacl-gallery.appspot.com/sine_synth/' 'sine_synth.html' }, 'prerelease_gallery': { 'hello_world': 'http://4.nacl-gallery.appspot.com/hello_world/' 'hello_world.html', 'hello_world_c': 'http://4.nacl-gallery.appspot.com/hello_world_c/' 'hello_world.html', 'life': 'http://4.nacl-gallery.appspot.com/life/life.html', 'pi_generator': 'http://4.nacl-gallery.appspot.com/pi_generator/' 'pi_generator.html', 'sine_synth': 'http://4.nacl-gallery.appspot.com/sine_synth/' 'sine_synth.html', 'geturl': 'http://4.nacl-gallery.appspot.com/geturl/geturl.html' } } def tearDown(self): pyauto.PyUITest.tearDown(self) self._RemoveDownloadedTestFile() def testNaClSDK(self): """Verify that NaCl SDK is working properly.""" if not self._HasAllSystemRequirements(): logging.info('System does not meet the requirements.') return self._extracted_sdk_path = tempfile.mkdtemp() self._VerifyDownloadLinks() self._VerifyNaClSDKInstaller() self._VerifyBuildStubProject() self._LaunchServerAndVerifyExamples() self._VerifyRebuildExamples() self._VerifySelldrAndNcval() def testVerifyNaClSDKChecksum(self): """Verify NaCl SDK Checksum.""" if not self._HasAllSystemRequirements(): logging.info('System does not meet the requirements.') return self._DownloadNaClSDK() if pyauto.PyUITest.IsWin(): expected_md5_key = self._settings['release_win_expected_md5_key'] file_path = os.path.join(self._temp_dir, 'naclsdk_win.exe') elif pyauto.PyUITest.IsMac(): expected_md5_key = self._settings['release_mac_expected_md5_key'] file_path = os.path.join(self._temp_dir, 'naclsdk_mac.tgz') elif pyauto.PyUITest.IsLinux(): expected_md5_key = self._settings['release_lin_expected_md5_key'] file_path = os.path.join(self._temp_dir, 'naclsdk_linux.tgz') else: self.fail(msg='NaCl SDK does not support this OS.') # Get expected MD5. expected_md5_url = self._settings['expected_md5_url'] response = urllib2.urlopen(expected_md5_url) dom = xml.dom.minidom.parseString(response.read()) dom_content = dom.getElementsByTagName('Contents') expected_md5 = None for con in dom_content: if (self._GetXMLNodeData(con.getElementsByTagName('Key')[0].childNodes) == expected_md5_key): node = con.getElementsByTagName('ETag')[0].childNodes expected_md5 = self._GetXMLNodeData(node).strip('"') self.assertTrue(expected_md5, msg='Cannot get expected MD5 from %s.' % expected_md5_url) md5 = hashlib.md5() md5.update(open(file_path).read()) md5_sum = md5.hexdigest() self.assertEqual(expected_md5, md5_sum, msg='Unexpected checksum. Expected: %s, got: %s' % (expected_md5, md5_sum)) def testVerifyNaClPlugin(self): """Verify NaCl plugin.""" if not self._HasAllSystemRequirements(): logging.info('System does not meet the requirements.') return self._OpenExamplesAndStartTest( self._settings['gallery_examples']) def testVerifyPrereleaseGallery(self): """Verify Pre-release gallery examples.""" if not self._HasAllSystemRequirements(): logging.info('System does not meet the requirements.') return self._OpenExamplesAndStartTest( self._settings['prerelease_gallery']) def _VerifyDownloadLinks(self): """Verify the download links.""" html = None for i in xrange(3): try: html = urllib2.urlopen(self._settings['post_sdk_download_url']).read() break except: pass self.assertTrue(html, msg='Cannot open URL: %s' % self._settings['post_sdk_download_url']) # Make sure the correct URL is under the correct label. if pyauto.PyUITest.IsWin(): win_sdk_url = self._settings['post_win_sdk_url'] win_url_index = html.find(win_sdk_url) self.assertTrue(win_url_index > -1, msg='Missing SDK download URL: %s' % win_sdk_url) win_keyword_index = html.rfind('Windows', 0, win_url_index) self.assertTrue(win_keyword_index > -1, msg='Misplaced download link: %s' % win_sdk_url) elif pyauto.PyUITest.IsMac(): mac_sdk_url = self._settings['post_mac_sdk_url'] mac_url_index = html.find(mac_sdk_url) self.assertTrue(mac_url_index > -1, msg='Missing SDK download URL: %s' % mac_sdk_url) mac_keyword_index = html.rfind('Macintosh', 0, mac_url_index) self.assertTrue(mac_keyword_index > -1, msg='Misplaced download link: %s' % mac_sdk_url) elif pyauto.PyUITest.IsLinux(): lin_sdk_url = self._settings['post_lin_sdk_url'] lin_url_index = html.find(lin_sdk_url) self.assertTrue(lin_url_index > -1, msg='Missing SDK download URL: %s' % lin_sdk_url) lin_keyword_index = html.rfind('Linux', 0, lin_url_index) self.assertTrue(lin_keyword_index > -1, msg='Misplaced download link: %s' % lin_sdk_url) else: self.fail(msg='NaCl SDK does not support this OS.') def _VerifyNaClSDKInstaller(self): """Verify NaCl SDK installer.""" search_list = [ 'build.scons', 'favicon.ico', 'geturl/', 'hello_world/', 'hello_world_c/', 'httpd.py', 'index.html', 'nacl_sdk_scons/', 'pi_generator/', 'scons', 'sine_synth/' ] mac_lin_additional_search_items = [ 'sel_ldr_x86_32', 'sel_ldr_x86_64', 'ncval_x86_32', 'ncval_x86_64' ] win_additional_search_items = [ 'httpd.cmd', 'sel_ldr_x86_32.exe', 'sel_ldr_x86_64.exe', 'ncval_x86_32.exe', 'ncval_x86_64.exe' ] self._DownloadNaClSDK() if pyauto.PyUITest.IsWin(): self._ExtractNaClSDK() self._SearchNaClSDKFileWindows( search_list + win_additional_search_items) elif pyauto.PyUITest.IsMac(): source_file = os.path.join(self._temp_dir, 'naclsdk_mac.tgz') self._SearchNaClSDKTarFile(search_list + mac_lin_additional_search_items, source_file) self._ExtractNaClSDK() elif pyauto.PyUITest.IsLinux(): source_file = os.path.join(self._temp_dir, 'naclsdk_linux.tgz') self._SearchNaClSDKTarFile(search_list + mac_lin_additional_search_items, source_file) self._ExtractNaClSDK() else: self.fail(msg='NaCl SDK does not support this OS.') def _VerifyBuildStubProject(self): """Build stub project.""" stub_project_files = [ 'build.scons', 'scons' ] project_template_path = self._GetDirectoryPath('project_templates', self._extracted_sdk_path) examples_path = self._GetDirectoryPath('examples', self._extracted_sdk_path) init_project_path = os.path.join(project_template_path, 'init_project.py') # Build a C project. subprocess.call( ['python', init_project_path, '-n', 'hello_c', '-c', '-d', examples_path], stdout=subprocess.PIPE) hello_c_path = os.path.join(examples_path, 'hello_c') for file in stub_project_files: self.assertTrue(self._HasFile(file, hello_c_path), msg='Cannot build C stub project.') # Build a C++ project. subprocess.call( ['python', init_project_path, '-n', 'hello_cc', '-d', examples_path], stdout=subprocess.PIPE) hello_cc_path = os.path.join(examples_path, 'hello_cc') for file in stub_project_files: self.assertTrue(self._HasFile(file, hello_cc_path), msg='Cannot build C++ stub project.') def _LaunchServerAndVerifyExamples(self): """Start local HTTP server and verify examples.""" # Make sure server is not open. if self._IsURLAlive('http://localhost:5103'): self._CloseHTTPServer() # Start HTTP server. examples_path = self._GetDirectoryPath('examples', self._extracted_sdk_path) if pyauto.PyUITest.IsWin(): http_path = os.path.join(examples_path, 'httpd.cmd') proc = subprocess.Popen([http_path], cwd=examples_path) else: http_path = os.path.join(examples_path, 'httpd.py') proc = subprocess.Popen(['python', http_path], cwd=examples_path) success = self.WaitUntil( lambda: self._IsURLAlive('http://localhost:5103'), timeout=30, retry_sleep=1, expect_retval=True) self.assertTrue(success, msg='Cannot open HTTP server. %s' % self.GetActiveTabTitle()) examples = { 'hello_world_c': 'http://localhost:5103/hello_world_c/' 'hello_world.html', 'hello_world': 'http://localhost:5103/hello_world/hello_world.html', 'geturl': 'http://localhost:5103/geturl/geturl.html', 'pi_generator': 'http://localhost:5103/pi_generator/pi_generator.html', 'sine_synth': 'http://localhost:5103/sine_synth/sine_synth.html', } try: self._OpenExamplesAndStartTest(examples) finally: self._CloseHTTPServer(proc) def _VerifyRebuildExamples(self): """Re-build the examples and verify they are as expected.""" example_dirs = [ 'geturl', 'hello_world', 'hello_world_c', 'pi_generator', 'sine_synth' ] examples_path = self._GetDirectoryPath('examples', self._extracted_sdk_path) scons_path = os.path.join(examples_path, 'scons -c') subprocess.call([scons_path], cwd=examples_path, shell=True) for x in example_dirs: ex_path = os.path.join(examples_path, x) self.assertFalse(self._HasFile('*.nmf', ex_path), msg='Failed running scons -c.') scons_path = os.path.join(examples_path, 'scons') proc = subprocess.Popen([scons_path], cwd=examples_path, stdout=subprocess.PIPE, shell=True) proc.communicate() # Verify each example directory contains .nmf file. for dir in example_dirs: dir = os.path.join(examples_path, dir) if not self._HasFile('*.nmf', dir): self.fail(msg='Failed running scons.') self._LaunchServerAndVerifyExamples() def _VerifySelldrAndNcval(self): """Verify sel_ldr and ncval.""" architecture = self._GetPlatformArchitecture() scons_arg = None if pyauto.PyUITest.IsWin(): if architecture == '64bit': scons_arg = 'test64' else: scons_arg = 'test32' elif pyauto.PyUITest.IsMac(): scons_arg = 'test64' elif pyauto.PyUITest.IsLinux(): scons_arg = 'test64' examples_path = self._GetDirectoryPath('examples', self._extracted_sdk_path) scons_path = os.path.join(examples_path, 'scons ' + scons_arg) # Build and run the unit test. proc = subprocess.Popen([scons_path], stdout=subprocess.PIPE, shell=True, cwd=examples_path) lines = proc.communicate()[0].splitlines() test_ran = False for line in lines: if 'Check:' in line: self.assertTrue('passed' in line, msg='Nacl-sel_ldr unit test failed.') test_ran = True self.assertTrue(test_ran, msg='Failed to build and run nacl-sel_ldr unit test.') if architecture == '64bit': self.assertTrue( self._HasPathInTree('hello_world_x86_64.nexe', True, root=examples_path), msg='Missing file: hello_world_x86_64.nexe.') else: self.assertTrue( self._HasPathInTree('hello_world_x86_32.nexe', True, root=examples_path), msg='Missing file: hello_world_x86_32.nexe.') # Verify that a mismatch of sel_ldr and .nexe produces an error. toolchain_path = self._GetDirectoryPath('toolchain', self._extracted_sdk_path) bin_path = self._GetDirectoryPath('bin', toolchain_path) hello_world_path = self._GetDirectoryPath('hello_world', examples_path) sel_32_path = os.path.join(bin_path, 'sel_ldr_x86_32') sel_64_path = os.path.join(bin_path, 'sel_ldr_x86_64') nexe_32_path = os.path.join(hello_world_path, 'hello_world_x86_32.nexe') nexe_64_path = os.path.join(hello_world_path, 'hello_world_x86_64.nexe') if architecture == '64bit': success = self._RunProcessAndCheckOutput( [sel_64_path, nexe_32_path], 'Error while loading') else: success = self._RunProcessAndCheckOutput( [sel_32_path, nexe_64_path], 'Error while loading') self.assertTrue(success, msg='Failed to verify sel_ldr and .nexe mismatch.') # Run the appropriate ncval for the platform on the matching .nexe. ncval_32_path = os.path.join(bin_path, 'ncval_x86_32') ncval_64_path = os.path.join(bin_path, 'ncval_x86_64') if architecture == '64bit': success = self._RunProcessAndCheckOutput( [ncval_64_path, nexe_64_path], 'is safe') else: success = self._RunProcessAndCheckOutput( [ncval_32_path, nexe_32_path], 'is safe') self.assertTrue(success, msg='Failed to verify ncval.') # Verify that a mismatch of ncval and .nexe produces an error. if architecture == '64bit': success = self._RunProcessAndCheckOutput( [ncval_64_path, nexe_32_path], 'is safe', is_in=False) else: success = self._RunProcessAndCheckOutput( [ncval_32_path, nexe_64_path], 'is safe', is_in=False) self.assertTrue(success, msg='Failed to verify ncval and .nexe mismatch.') def _RemoveDownloadedTestFile(self): """Delete downloaded files and dirs from downloads directory.""" if self._extracted_sdk_path and os.path.exists(self._extracted_sdk_path): self._CloseHTTPServer() def _RemoveFile(): shutil.rmtree(self._extracted_sdk_path, ignore_errors=True) return os.path.exists(self._extracted_sdk_path) success = self.WaitUntil(_RemoveFile, retry_sleep=2, expect_retval=False) self.assertTrue(success, msg='Cannot remove %s' % self._extracted_sdk_path) if self._temp_dir: pyauto_utils.RemovePath(self._temp_dir) def _RunProcessAndCheckOutput(self, args, look_for, is_in=True): """Run process and look for string in output. Args: args: Argument strings to pass to subprocess. look_for: The string to search in output. is_in: True if checking if param look_for is in output. False if checking if param look_for is not in output. Returns: True, if output contains parameter |look_for| and |is_in| is True, or False otherwise. """ proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = proc.communicate() lines = (stdout + stderr).splitlines() for line in lines: if look_for in line: return is_in return not is_in def _OpenExamplesAndStartTest(self, examples): """Open each example and verify that it's working. Args: examples: A dict of name to url of examples. """ self._EnableNaClPlugin() # Open all examples. for name, url in examples.items(): self.AppendTab(pyauto.GURL(url)) self._CheckForCrashes() # Verify all examples are working. for name, url in examples.items(): self._VerifyAnExample(name, url) self._CheckForCrashes() # Reload all examples. for _ in range(2): for tab_index in range(self.GetTabCount()): self.GetBrowserWindow(0).GetTab(tab_index).Reload() self._CheckForCrashes() # Verify all examples are working. for name, url in examples.items(): self._VerifyAnExample(name, url) self._CheckForCrashes() # Close each tab, check for crashes and verify all open # examples operate correctly. tab_count = self.GetTabCount() for index in xrange(tab_count - 1, 0, -1): self.GetBrowserWindow(0).GetTab(index).Close(True) self._CheckForCrashes() tabs = self.GetBrowserInfo()['windows'][0]['tabs'] for tab in tabs: if tab['index'] > 0: for name, url in examples.items(): if url == tab['url']: self._VerifyAnExample(name, url) break def _VerifyAnExample(self, name, url): """Verify NaCl example is working. Args: name: A string name of the example. url: A string url of the example. """ available_example_tests = { 'hello_world_c': self._VerifyHelloWorldExample, 'hello_world': self._VerifyHelloWorldExample, 'pi_generator': self._VerifyPiGeneratorExample, 'sine_synth': self._VerifySineSynthExample, 'geturl': self._VerifyGetURLExample, 'life': self._VerifyConwaysLifeExample } if not name in available_example_tests: self.fail(msg='No test available for %s.' % name) info = self.GetBrowserInfo() tabs = info['windows'][0]['tabs'] tab_index = None for tab in tabs: if url == tab['url']: self.ActivateTab(tab['index']) tab_index = tab['index'] break if tab_index: available_example_tests[name](tab_index, name, url) def _VerifyHelloWorldExample(self, tab_index, name, url): """Verify Hello World Example. Args: tab_index: Tab index integer that the example is on. name: A string name of the example. url: A string url of the example. """ success = self.WaitUntil( lambda: self.GetDOMValue( 'document.getElementById("statusField").innerHTML', 0, tab_index), timeout=60, expect_retval='SUCCESS') self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) js_code = """ window.alert = function(e) { window.domAutomationController.send(String(e)); } window.domAutomationController.send("done"); """ self.ExecuteJavascript(js_code, 0, tab_index) result = self.ExecuteJavascript('document.helloForm.elements[1].click();', 0, tab_index) self.assertEqual(result, '42', msg='Example %s failed. URL: %s' % (name, url)) result = self.ExecuteJavascript('document.helloForm.elements[2].click();', 0, tab_index) self.assertEqual(result, 'dlrow olleH', msg='Example %s failed. URL: %s' % (name, url)) def _VerifyPiGeneratorExample(self, tab_index, name, url): """Verify Pi Generator Example. Args: tab_index: Tab index integer that the example is on. name: A string name of the example. url: A string url of the example. """ success = self.WaitUntil( lambda: self.GetDOMValue('document.form.pi.value', 0, tab_index)[0:3], timeout=120, expect_retval='3.1') self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) # Get top corner of Pi image. js_code = """ var obj = document.getElementById('piGenerator'); var curleft = curtop = 0; do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); window.domAutomationController.send(curleft + ", " + curtop); """ result = self.ExecuteJavascript(js_code, 0, tab_index) result_split = result.split(', ') x = int(result_split[0]) y = int(result_split[1]) window = self.GetBrowserInfo()['windows'][0] window_to_content_x = 2 window_to_content_y = 80 pi_image_x = x + window['x'] + window_to_content_x pi_image_y = y + window['y'] + window_to_content_y if self._IsGetPixelSupported(): is_animating = self._IsColorChanging(pi_image_x, pi_image_y, 50, 50) self.assertTrue(is_animating, msg='Example %s failed. URL: %s' % (name, url)) def _VerifySineSynthExample(self, tab_index, name, url): """Verify Sine Wave Synthesizer Example. Args: tab_index: Tab index integer that the example is on. name: A string name of the example. url: A string url of the example. """ success = self.WaitUntil( lambda: self.GetDOMValue( 'document.getElementById("frequency_field").value', 0, tab_index), timeout=30, expect_retval='440') self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) self.ExecuteJavascript( 'document.body.getElementsByTagName("button")[0].click();' 'window.domAutomationController.send("done")', 0, tab_index) # TODO(chrisphan): Verify sound. def _VerifyGetURLExample(self, tab_index, name, url): """Verify GetURL Example. Args: tab_index: Tab index integer that the example is on. name: A string name of the example. url: A string url of the example. """ success = self.WaitUntil( lambda: self.GetDOMValue( 'document.getElementById("status_field").innerHTML', 0, tab_index), timeout=60, expect_retval='SUCCESS') self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) self.ExecuteJavascript( 'document.geturl_form.elements[0].click();' 'window.domAutomationController.send("done")', 0, tab_index) js_code = """ var output = document.getElementById("general_output").innerHTML; var result; if (output.indexOf("test passed") != -1) result = "pass"; else result = "fail"; window.domAutomationController.send(result); """ success = self.WaitUntil( lambda: self.ExecuteJavascript(js_code, 0, tab_index), timeout=30, expect_retval='pass') self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) def _VerifyConwaysLifeExample(self, tab_index, name, url): """Verify Conway's Life Example. Args: tab_index: Tab index integer that the example is on. name: A string name of the example. url: A string url of the example. """ window = self.GetBrowserInfo()['windows'][0] window_to_content_x = 2 window_to_content_y = 80 x = window['x'] + window_to_content_x y = window['y'] + window_to_content_y offset_pixel = 100 if self._IsGetPixelSupported(): success = self.WaitUntil( lambda: self._GetPixel(x + offset_pixel, y + offset_pixel), timeout=30, expect_retval=16777215) self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url)) def _GetXMLNodeData(self, nodelist): rc = [] for node in nodelist: if node.nodeType == node.TEXT_NODE: rc.append(node.data) return ''.join(rc) def _IsColorChanging(self, x, y, width, height): """Check screen for anything that is moving. Args: x: X coordinate on the screen. y: Y coordinate on the screen. width: Width of the area to scan. height: Height of the area to scan. Returns: True, if pixel color in area is changing, or False otherwise. """ color_a = self._GetAreaPixelColor(x, y, width, height) def _HasColorChanged(): color_b = self._GetAreaPixelColor(x, y, width, height) return color_a != color_b return self.WaitUntil(_HasColorChanged, timeout=6, retry_sleep=2, expect_retval=True) def _IsGetPixelSupported(self): """Check if get pixel is supported. Returns: True, if get pixel is supported, or False otherwise. """ return pyauto.PyUITest.IsWin() def _GetAreaPixelColor(self, x, y, width, height): """Get an area of pixel color and return a list of color code values. Args: x: X coordinate on the screen. y: Y coordinate on the screen. width: Width of the area to scan. height: Height of the area to scan. Returns: A list containing color codes. """ if pyauto.PyUITest.IsMac(): pass # TODO(chrisphan): Do Mac. elif pyauto.PyUITest.IsWin(): return self._GetAreaPixelColorWin(x, y, width, height) elif pyauto.PyUITest.IsLinux(): pass # TODO(chrisphan): Do Linux. return None def _GetAreaPixelColorWin(self, x, y, width, height): """Get an area of pixel color for Windows and return a list. Args: x: X coordinate on the screen. y: Y coordinate on the screen. width: Width of the area to scan. height: Height of the area to scan. Returns: A list containing color codes. """ colors = [] hdc = ctypes.windll.user32.GetDC(0) for x_pos in xrange(x, x + width, 1): for y_pos in xrange(y, y + height, 1): color = ctypes.windll.gdi32.GetPixel(hdc, x_pos, y_pos) colors.append(color) return colors def _GetPixel(self, x, y): """Get pixel color at coordinate x and y. Args: x: X coordinate on the screen. y: Y coordinate on the screen. Returns: An integer color code. """ if pyauto.PyUITest.IsMac(): pass # TODO(chrisphan): Do Mac. elif pyauto.PyUITest.IsWin(): return self._GetPixelWin(x, y) elif pyauto.PyUITest.IsLinux(): pass # TODO(chrisphan): Do Linux. return None def _GetPixelWin(self, x, y): """Get pixel color at coordinate x and y for Windows Args: x: X coordinate on the screen. y: Y coordinate on the screen. Returns: An integer color code. """ hdc = ctypes.windll.user32.GetDC(0) color = ctypes.windll.gdi32.GetPixel(hdc, x, y) return color def _CheckForCrashes(self, last_action=None, last_action_param=None): """Check for any browser/tab crashes and hangs. Args: last_action: Specify action taken before checking for crashes. last_action_param: Parameter for last action. """ self.assertTrue(self.GetBrowserWindowCount(), msg='Browser crashed, no window is open.') info = self.GetBrowserInfo() breakpad_folder = info['properties']['DIR_CRASH_DUMPS'] old_dmp_files = glob.glob(os.path.join(breakpad_folder, '*.dmp')) # Verify there're no crash dump files. for dmp_file in glob.glob(os.path.join(breakpad_folder, '*.dmp')): self.assertTrue(dmp_file in old_dmp_files, msg='Crash dump %s found' % dmp_file) # Check for any crashed tabs. tabs = info['windows'][0]['tabs'] for tab in tabs: if tab['url'] != 'about:blank': if not self.GetDOMValue('document.body.innerHTML', 0, tab['index']): self.fail(msg='Tab crashed on %s' % tab['url']) # TODO(chrisphan): Check for tab hangs and browser hangs. # TODO(chrisphan): Handle specific action: close browser, close tab. if last_action == 'close tab': pass elif last_action == 'close browser': pass else: pass def _GetPlatformArchitecture(self): """Get platform architecture. Args: last_action: Last action taken before checking for crashes. last_action_param: Parameter for last action. Returns: A string representing the platform architecture. """ if pyauto.PyUITest.IsWin(): if os.environ['PROGRAMFILES'] == 'C:\\Program Files (x86)': return '64bit' else: return '32bit' elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux(): if platform.machine() == 'x86_64': return '64bit' else: return '32bit' return '32bit' def _HasFile(self, pattern, root=os.curdir): """Check if a file matching the specified pattern exists in a directory. Args: pattern: Pattern of file name. root: Directory to start looking. Returns: True, if root contains the file name pattern, or False otherwise. """ return len(glob.glob(os.path.join(root, pattern))) def _HasPathInTree(self, pattern, is_file, root=os.curdir): """Recursively checks if a file/directory matching a pattern exists. Args: pattern: Pattern of file or directory name. is_file: True if looking for file, or False if looking for directory. root: Directory to start looking. Returns: True, if root contains the directory name pattern, or False otherwise. """ for path, dirs, files in os.walk(os.path.abspath(root)): if is_file: if len(fnmatch.filter(files, pattern)): return True else: if len(fnmatch.filter(dirs, pattern)): return True return False def _GetDirectoryPath(self, pattern, root=os.curdir): """Get the path of a directory in another directory. Args: pattern: Pattern of directory name. root: Directory to start looking. Returns: A string of the path. """ for path, dirs, files in os.walk(os.path.abspath(root)): result = fnmatch.filter(dirs, pattern) if result: return os.path.join(path, result[0]) return None def _HasAllSystemRequirements(self): """Verify NaCl SDK installation system requirements. Returns: True, if system passed requirements, or False otherwise. """ # Check python version. if sys.version_info[0:2] < (2, 6): return False # Check OS requirements. if pyauto.PyUITest.IsMac(): mac_min_version = version.StrictVersion('10.6') mac_version = version.StrictVersion(platform.mac_ver()[0]) if mac_version < mac_min_version: return False elif pyauto.PyUITest.IsWin(): if not (self.IsWin7() or self.IsWinVista() or self.IsWinXP()): return False elif pyauto.PyUITest.IsLinux(): pass # TODO(chrisphan): Check Lin requirements. else: return False # Check for Chrome version compatibility. # NaCl supports Chrome 10 and higher builds. min_required_chrome_build = self._settings['min_required_chrome_build'] browser_info = self.GetBrowserInfo() chrome_version = browser_info['properties']['ChromeVersion'] chrome_build = int(chrome_version.split('.')[0]) return chrome_build >= min_required_chrome_build def _DownloadNaClSDK(self): """Download NaCl SDK.""" self._temp_dir = tempfile.mkdtemp() if pyauto.PyUITest.IsWin(): dl_file = urllib2.urlopen(self._settings['release_win_sdk_url']) file_path = os.path.join(self._temp_dir, 'naclsdk_win.exe') elif pyauto.PyUITest.IsMac(): dl_file = urllib2.urlopen(self._settings['release_mac_sdk_url']) file_path = os.path.join(self._temp_dir, 'naclsdk_mac.tgz') elif pyauto.PyUITest.IsLinux(): dl_file = urllib2.urlopen(self._settings['release_lin_sdk_url']) file_path = os.path.join(self._temp_dir, 'naclsdk_linux.tgz') else: self.fail(msg='NaCl SDK does not support this OS.') try: f = open(file_path, 'wb') f.write(dl_file.read()) except IOError: self.fail(msg='Cannot open %s.' % file_path) finally: f.close() def _ExtractNaClSDK(self): """Extract NaCl SDK.""" if pyauto.PyUITest.IsWin(): source_file = os.path.join(self._temp_dir, 'naclsdk_win.exe') proc = subprocess.Popen([source_file, '/S', '/D=' + self._extracted_sdk_path], stdout=subprocess.PIPE) proc.communicate() if not os.listdir(self._extracted_sdk_path): self.fail(msg='Failed to extract NaCl SDK.') elif pyauto.PyUITest.IsMac(): source_file = os.path.join(self._temp_dir, 'naclsdk_mac.tgz') tar = tarfile.open(source_file, 'r') tar.extractall(self._extracted_sdk_path) elif pyauto.PyUITest.IsLinux(): source_file = os.path.join(self._temp_dir, 'naclsdk_linux.tgz') tar = tarfile.open(source_file, 'r') tar.extractall(self._extracted_sdk_path) else: self.fail(msg='NaCl SDK does not support this OS.') def _IsURLAlive(self, url): """Test if URL is alive.""" try: urllib2.urlopen(url) except: return False return True def _CloseHTTPServer(self, proc=None): """Close HTTP server. Args: proc: Process that opened the HTTP server. proc is None when there is no pointer to HTTP server process. """ if not self._IsURLAlive('http://localhost:5103'): return response = urllib2.urlopen('http://localhost:5103') html = response.read() if not 'Native Client' in html: self.fail(msg='Port 5103 is in use.') urllib2.urlopen('http://localhost:5103?quit=1') success = self.WaitUntil( lambda: self._IsURLAlive('http://localhost:5103'), timeout=30, retry_sleep=1, expect_retval=False) if not success: if not proc: self.fail(msg='Failed to close HTTP server.') else: if proc.poll() == None: try: proc.kill() except: self.fail(msg='Failed to close HTTP server') def _SearchNaClSDKTarFile(self, search_list, source_file): """Search NaCl SDK tar file for example files and directories. Args: search_list: A list of strings, representing file and directory names for which to search. source_file: The string name of an NaCl SDK tar file. """ tar = tarfile.open(source_file, 'r') # Look for files and directories in examples. files = copy.deepcopy(search_list) for tar_info in tar: file_name = tar_info.name if tar_info.isdir() and not file_name.endswith('/'): file_name = file_name + '/' for name in files: if file_name.find('examples/' + name): files.remove(name) if not files: break tar.close() self.assertEqual(len(files), 0, msg='Missing files or directories: %s' % ', '.join(map(str, files))) def _SearchNaClSDKFileWindows(self, search_list): """Search NaCl SDK file for example files and directories in Windows. Args: search_list: A list of strings, representing file and directory names for which to search. """ missing_items = [] for name in search_list: is_file = name.find('/') < 0 if not is_file: name = name.replace('/', '') if not self._HasPathInTree(name, is_file, self._extracted_sdk_path): missing_items.append(name) self.assertEqual(len(missing_items), 0, msg='Missing files or directories: %s' % ', '.join(map(str, missing_items))) def _EnableNaClPlugin(self): """Enable NaCl plugin.""" nacl_plugin = (self.GetPluginsInfo().PluginForName('Chrome NaCl') or self.GetPluginsInfo().PluginForName('Native Client')) if not nacl_plugin: self.fail(msg='No NaCl plugin found.') self.EnablePlugin(nacl_plugin[0]['path']) self.NavigateToURL('about:flags') js_code = """ chrome.send('enableFlagsExperiment', ['enable-nacl', 'true']); requestFlagsExperimentsData(); window.domAutomationController.send('done'); """ self.ExecuteJavascript(js_code) self.RestartBrowser(False) self.NavigateToURL('about://version') self.assertEqual(self.FindInPage('--enable-nacl')['match_count'], 1, msg='Missing expected Chrome flag --enable-nacl.') if __name__ == '__main__': pyauto_functional.Main()