1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import copy
7import ctypes
8from distutils import version
9import fnmatch
10import glob
11import hashlib
12import logging
13import os
14import platform
15import re
16import shutil
17import subprocess
18import sys
19import tempfile
20import urllib2
21import xml.dom.minidom
22import zipfile
23
24import pyauto_functional  # Must be imported before pyauto.
25import pyauto
26import pyauto_utils
27import test_utils
28
29
30class NaClSDKTest(pyauto.PyUITest):
31  """Tests for the NaCl SDK."""
32  _isExamplesTest = False
33  _extracted_sdk_path = None
34  _temp_dir = None
35  _updated_pepper_versions = []
36  _latest_updated_pepper_versions = []
37  _settings = {
38      'post_sdk_download_url': 'http://code.google.com/chrome/nativeclient/'
39          'docs/download.html',
40      'post_sdk_zip': 'http://storage.googleapis.com/'
41          'nativeclient-mirror/nacl/nacl_sdk/nacl_sdk.zip',
42      'min_required_chrome_build': 14,
43  }
44
45  def tearDown(self):
46    pyauto.PyUITest.tearDown(self)
47    if not self._isExamplesTest:
48      self._RemoveDownloadedTestFile()
49
50  def testNaClSDK(self):
51    """Verify that NaCl SDK is working properly."""
52    if not self._HasAllSystemRequirements():
53      logging.info('System does not meet the requirements.')
54      return
55    self._extracted_sdk_path = tempfile.mkdtemp()
56    self._VerifyDownloadLinks()
57    self._VerifyNaClSDKInstaller()
58    self._VerifyInstall()
59    self._VerifyUpdate()
60    self._LaunchServerAndVerifyExamplesAllPepperVersions()
61
62  def NaClSDKExamples(self):
63    """Verify if NaCl SDK examples are working."""
64    self._isExamplesTest = True
65    nacl_sdk_root = os.environ.get('NACL_SDK_ROOT', None)
66    pepper_version = os.environ.get('PEPPER_VER', None)
67    if nacl_sdk_root and pepper_version:
68      self._LaunchServerAndVerifyExamples('pepper_' + pepper_version,
69          nacl_sdk_root)
70    else:
71      self.fail(msg='Missing pepper version to be checked or SDK path.')
72
73  def _VerifyDownloadLinks(self):
74    """Verify the download links.
75
76    Simply verify that NaCl download links exist in html page.
77    """
78    html = None
79    for i in xrange(3):
80      try:
81        html = urllib2.urlopen(self._settings['post_sdk_download_url']).read()
82        break
83      except:
84        pass
85    self.assertTrue(html,
86                    msg='Cannot open URL: %s' %
87                    self._settings['post_sdk_download_url'])
88    sdk_url = self._settings['post_sdk_zip']
89    self.assertTrue(sdk_url in html,
90                    msg='Missing SDK download URL: %s' % sdk_url)
91
92  def _VerifyNaClSDKInstaller(self):
93    """Verify NaCl SDK installer."""
94    search_list = [
95        'sdk_cache/',
96        'sdk_tools/',
97    ]
98    mac_lin_additional_search_items = [
99        'naclsdk',
100    ]
101    win_additional_search_items = [
102        'naclsdk.bat'
103    ]
104    self._DownloadNaClSDK()
105    self._ExtractNaClSDK()
106    if pyauto.PyUITest.IsWin():
107      self._SearchNaClSDKFile(
108          search_list + win_additional_search_items)
109    elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux():
110      self._SearchNaClSDKFile(
111          search_list + mac_lin_additional_search_items)
112    else:
113      self.fail(msg='NaCl SDK does not support this OS.')
114
115  def _VerifyInstall(self):
116    """Install NACL sdk."""
117    # Executing naclsdk(.bat) list
118    if pyauto.PyUITest.IsWin():
119      source_file = os.path.join(
120          self._extracted_sdk_path, 'nacl_sdk', 'naclsdk.bat')
121    elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux():
122      source_file = os.path.join(
123          self._extracted_sdk_path, 'nacl_sdk', 'naclsdk')
124      subprocess.call(['chmod', '-R', '755', self._extracted_sdk_path])
125    else:
126      self.fail(msg='NaCl SDK does not support this OS.')
127    subprocess.Popen([source_file, 'list'],
128                     stdout=subprocess.PIPE,
129                     stderr=subprocess.PIPE).communicate()
130
131  def _VerifyUpdate(self):
132    """Update NACL sdk"""
133    # Executing naclsdk(.bat) update
134    if pyauto.PyUITest.IsWin():
135      source_file = os.path.join(self._extracted_sdk_path, 'nacl_sdk',
136                                 'naclsdk.bat')
137    elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux():
138      source_file = os.path.join(self._extracted_sdk_path, 'nacl_sdk',
139                                 'naclsdk')
140    else:
141      self.fail(msg='NaCl SDK does not support this OS.')
142    # Executing nacl_sdk(.bat) update to get the latest version.
143    updated_output = subprocess.Popen([source_file, 'update'],
144        stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
145    self._updated_pepper_versions.extend(
146        re.findall('Updating bundle (pepper_[0-9]{2})', updated_output))
147    self._updated_pepper_versions = list(set(self._updated_pepper_versions))
148    self._updated_pepper_versions.sort(key=str.lower)
149    updated_pepper_versions_len = len(self._updated_pepper_versions)
150    self._latest_updated_pepper_versions = filter(
151        lambda x: x >= 'pepper_18', self._updated_pepper_versions)
152
153  def _GetURLForExampleName(self, name, toolchain):
154    return 'http://localhost:5103/%s/index_%s.html' % (name, toolchain)
155
156  def _GetExampleNamesAndURLs(self, examples_path):
157    """Get a list of all examples as (name, url) tuples.
158
159    Args:
160      examples_path: The path to the examples directory in the NaCl SDK.
161    """
162    toolchains = ['newlib', 'glibc', 'pnacl']
163
164    examples = []
165    for toolchain in toolchains:
166      for example in os.listdir(examples_path):
167        html_path = os.path.join(examples_path, example,
168            'index_%s.html' % (toolchain,))
169        if os.path.exists(html_path):
170          example_url = self._GetURLForExampleName(example, toolchain)
171          examples.append((example, example_url))
172    return examples
173
174  def _LaunchServerAndVerifyExamplesAllPepperVersions(self):
175    for pepper_version in self._latest_updated_pepper_versions:
176      pepper_path = os.path.join(self._extracted_sdk_path,
177          'nacl_sdk', 'pepper_' + str(pepper_version))
178      self._LaunchServerAndVerifyExamples(pepper_version, pepper_path)
179
180  def _LaunchServerAndVerifyExamples(self, pepper_version, pepper_path):
181    """Start local HTTP server and verify examples."""
182    if self._ChromeAndPepperVersionMatch(pepper_version):
183      # Close server if it's already open.
184      if self._IsURLAlive('http://localhost:5103'):
185        self._CloseHTTPServer()
186
187      examples_path = os.path.join(pepper_path, 'examples')
188
189      # Launch local http server.
190      proc = subprocess.Popen(['make RUN'], shell=True, cwd=examples_path)
191      self.WaitUntil(
192          lambda: self._IsURLAlive('http://localhost:5103'),
193          timeout=150, retry_sleep=1)
194
195      examples = self._GetExampleNamesAndURLs(examples_path)
196      try:
197        self._OpenExamplesAndStartTest(examples)
198      finally:
199        self._CloseHTTPServer(proc)
200
201    else:
202      self.pprint('Pepper Version %s does not match the Chrome version %s.'
203          % (pepper_version,
204          self.GetBrowserInfo()['properties']['ChromeVersion']))
205
206  def _ChromeAndPepperVersionMatch(self, pepper_version='pepper_18'):
207    """Determine if chrome and pepper version match"""
208    version_number = re.findall('pepper_([0-9]{2})', pepper_version)
209    browser_info = self.GetBrowserInfo()
210    chrome_version = browser_info['properties']['ChromeVersion']
211    chrome_build = int(chrome_version.split('.')[0])
212    return int(chrome_build) == int(version_number[0])
213
214  def _RemoveDownloadedTestFile(self):
215    """Delete downloaded files and dirs from downloads directory."""
216    if self._extracted_sdk_path and os.path.exists(self._extracted_sdk_path):
217      self._CloseHTTPServer()
218
219      def _RemoveFile():
220        shutil.rmtree(self._extracted_sdk_path, ignore_errors=True)
221        return os.path.exists(self._extracted_sdk_path)
222
223      success = self.WaitUntil(_RemoveFile, retry_sleep=2,
224                               expect_retval=False)
225      self.assertTrue(success,
226                      msg='Cannot remove %s' % self._extracted_sdk_path)
227
228    if self._temp_dir:
229      pyauto_utils.RemovePath(self._temp_dir)
230
231  def _OpenExamplesAndStartTest(self, examples):
232    """Open each example and verify that it's working.
233
234    Args:
235      examples: A list of example (name, url) tuples.
236    """
237    example_verify_funcs = {
238        'dlopen': self._VerifyDynamicLibraryOpen,
239        'file_io': self._VerifyFileIoExample,
240        'geturl': self._VerifyGetURLExample,
241        'input_events': self._VerifyInputEventsExample,
242        'load_progress': self._VerifyLoadProgressExample,
243        'mt_input_events': self._VerifyMultithreadedInputEventsExample,
244        'pi_generator': self._VerifyPiGeneratorExample,
245        'sine_synth': self._VerifySineSynthExample,
246        'websocket': self._VerifyWebSocketExample,
247    }
248
249    # Remove examples that we don't yet verify
250    examples = [(name, url) for name, url in examples
251        if name in example_verify_funcs]
252
253    # Open all examples.
254    for name, url in examples:
255      self.AppendTab(pyauto.GURL(url))
256      self._CheckForCrashes()
257
258    # Verify all examples are working.
259    for name, url in examples:
260      self._VerifyAnExample(name, url, example_verify_funcs[name])
261    self._CheckForCrashes()
262
263    # Close each tab and check for crashes.
264    tab_count = self.GetTabCount()
265    for index in xrange(tab_count - 1, 0, -1):
266      self.CloseTab(tab_index=index)
267      self._CheckForCrashes()
268
269  def _VerifyAnExample(self, name, url, verify_func):
270    """Verify NaCl example is working.
271
272    Args:
273      name: A string name of the example.
274      url: A string url of the example.
275      verify_func: The function to verify the example.
276          Takes (tab_index, name, url) as parameters.
277    """
278    if not verify_func:
279      self.fail(msg='No test available for %s.' % name)
280
281    info = self.GetBrowserInfo()
282    tabs = info['windows'][0]['tabs']
283    tab_index = None
284    for tab in tabs:
285      if url == tab['url']:
286        self.ActivateTab(tab['index'])
287        tab_index = tab['index']
288        break
289
290    if tab_index:
291      verify_func(tab_index, name, url)
292
293  def _VerifyElementPresent(self, element_id, expected_value, tab_index, msg,
294                            attribute='innerHTML', timeout=150):
295    """Determine if dom element has the expected value.
296
297    Args:
298      element_id: Dom element's id.
299      expected_value: String to be matched against the Dom element.
300      tab_index: Tab index to work on.
301      attribute: Attribute to match |expected_value| against, if
302                 given. Defaults to 'innerHTML'.
303      timeout: The max timeout (in secs) for which to wait.
304    """
305    js_code = """
306        var output = document.getElementById('%s').%s;
307        var result;
308        if (output.indexOf('%s') != -1)
309          result = 'pass';
310        else
311          result = 'fail';
312        window.domAutomationController.send(result);
313    """ % (element_id, attribute, expected_value)
314    success = self.WaitUntil(
315        lambda: self.ExecuteJavascript(js_code, tab_index),
316        timeout=timeout, expect_retval='pass')
317    self.assertTrue(success, msg=msg)
318
319  def _CreateJSToSimulateMouseclick(self):
320    """Create javascript to simulate mouse click event."""
321    js_code = """
322        var rightClick = document.createEvent('MouseEvents');
323        rightClick.initMouseEvent(
324          'mousedown', true, true, document,
325          1, 32, 121, 10, 100,
326          false, false, false, false,
327          2, common.naclModule
328        );
329        common.naclModule.dispatchEvent(rightClick);
330        window.domAutomationController.send('done');
331    """
332    return js_code
333
334  def _VerifyInputEventsExample(self, tab_index, name, url):
335    """Verify Input Events Example.
336
337    Args:
338      tab_index: Tab index integer that the example is on.
339      name: A string name of the example.
340      url: A string url of the example.
341    """
342    success = self._VerifyElementPresent('eventString', 'DidChangeView',
343        tab_index, msg='Example %s failed. URL: %s' % (name, url))
344
345    # Simulate mouse click on event module.
346    js_code = self._CreateJSToSimulateMouseclick()
347    self.ExecuteJavascript(js_code, tab_index)
348
349    # Check if 'eventString' has handled above mouse click.
350    success = self.WaitUntil(
351        lambda: re.search('DidHandleInputEvent', self.GetDOMValue(
352          'document.getElementById("eventString").innerHTML',
353          tab_index)).group(), expect_retval='DidHandleInputEvent')
354    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
355
356  def _VerifyMultithreadedInputEventsExample(self, tab_index, name, url):
357    """Verify Input Events Example.
358
359    Args:
360      tab_index: Tab index integer that the example is on.
361      name: A string name of the example.
362      url: A string url of the example.
363    """
364    success = self.WaitUntil(
365        lambda: bool(self.GetDOMValue(
366          'document.getElementById("eventString").innerHTML',
367          tab_index).find('DidChangeView') + 1))
368
369    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
370
371    # Simulate mouse click on event module.
372    js_code = self._CreateJSToSimulateMouseclick()
373    self.ExecuteJavascript(js_code, tab_index)
374
375    # Check if above mouse click is handled.
376    success = self._VerifyElementPresent('eventString', 'Mouse event',
377        tab_index, msg='Example %s failed. URL: %s' % (name, url))
378
379    # Kill worker thread and queue
380    js_code = """
381      document.getElementsByTagName('button')[0].click();
382      window.domAutomationController.send('done');
383    """
384    self.ExecuteJavascript(js_code, tab_index)
385
386    # Check if main thread has cancelled queue.
387    success = self._VerifyElementPresent('eventString', 'Received cancel',
388        tab_index, msg='Example %s failed. URL: %s' % (name, url))
389
390    # Simulate mouse click on event module.
391    js_code = self._CreateJSToSimulateMouseclick()
392    self.ExecuteJavascript(js_code, tab_index)
393
394    # Check if above mouse click is not handled after killing worker thread.
395    def _CheckMouseClickEventStatus():
396      return self.GetDOMValue(
397        'document.getElementById("eventString").innerHTML',
398        tab_index).find('Mouse event', self.GetDOMValue(
399        'document.getElementById("eventString").innerHTML', tab_index).find(
400        'Received cancel'))
401
402    success = self.WaitUntil(_CheckMouseClickEventStatus, expect_retval=-1)
403    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
404
405  def _VerifyFileIoExample(self, tab_index, name, url):
406    """Verify File IO Example.
407
408    Args:
409      tab_index: Tab index integer that the example is on.
410      name: A string name of the example.
411      url: A string url of the example.
412    """
413    def _CheckStatus(substring_expected, fail_msg):
414      self.assertTrue(
415          self.WaitUntil(
416            lambda: self.GetDOMValue(
417              'document.getElementById("statusField").innerHTML', tab_index)\
418                  .find(substring_expected) != -1, expect_retval=True),
419          msg='Example %s failed. URL: %s. Reason: %s' % (name, url, fail_msg))
420
421    # Give permission to use file system by clicking infobar OK
422    infobar_index = test_utils.WaitForInfobarTypeAndGetIndex(self,
423        'confirm_infobar', 0, tab_index)
424    self.PerformActionOnInfobar('accept', infobar_index, 0, tab_index)
425    _CheckStatus('Ready!', 'NaCl module load')
426
427    # Check that deleting non-existing files gives file not found
428    js_code = """
429      document.getElementById('file_name').value = '/abc';
430      document.getElementById('file_editor').value = 'test';
431      document.getElementById('delete_but').click();
432      window.domAutomationController.send('done');
433    """
434    self.ExecuteJavascript(js_code, tab_index)
435    _CheckStatus('File not found', 'Delete non-existing')
436
437    # Check that saving works
438    js_code = """
439      document.getElementById('save_but').click();
440      window.domAutomationController.send('done');
441    """
442    self.ExecuteJavascript(js_code, tab_index)
443    _CheckStatus('Save successful', 'Save test')
444
445    # Check that we load what we saved
446    js_code = """
447      document.getElementById('file_editor').value = 'different';
448      document.getElementById('load_but').click();
449      window.domAutomationController.send('done');
450    """
451    self.ExecuteJavascript(js_code, tab_index)
452    _CheckStatus('Load complete', 'Load test')
453    self.assertTrue(
454        self.GetDOMValue('document.getElementById("file_editor").value',
455          tab_index).find('test') != -1, msg='Loaded wrong text or failed')
456
457    # Check that we delete files successfully
458    js_code = """
459      document.getElementById('delete_but').click();
460      window.domAutomationController.send('done');
461    """
462    self.ExecuteJavascript(js_code, tab_index)
463    _CheckStatus('File deleted', 'Delete test')
464
465    # Check that file is deleted and load produces not found
466    js_code = """
467      document.getElementById('load_but').click();
468      window.domAutomationController.send('done');
469    """
470    self.ExecuteJavascript(js_code, tab_index)
471    _CheckStatus('File not found', 'Load deleted test')
472
473  def _VerifyWebSocketExample(self, tab_index, name, url):
474    """Verify Web Socket Open Example.
475
476    Args:
477      tab_index: Tab index integer that the example is on.
478      name: A string name of the example.
479      url: A string url of the example.
480    """
481    # Check if example is loaded.
482    success = self.WaitUntil(
483        lambda: self.GetDOMValue(
484            'document.getElementById("statusField").innerHTML', tab_index),
485            expect_retval='SUCCESS')
486    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
487
488    # Simulate clicking on Connect button to establish a connection.
489    js_code = """
490      document.getElementsByTagName('input')[1].click();
491      window.domAutomationController.send('done');
492    """
493    self.ExecuteJavascript(js_code, tab_index)
494
495    # Check if connected
496    success = self._VerifyElementPresent('log', 'connected', tab_index,
497        msg='Example %s failed. URL: %s' % (name, url))
498
499    # Simulate clicking on Send button to send text message in log.
500    js_code = """
501      document.getElementsByTagName('input')[3].click();
502      window.domAutomationController.send('done');
503    """
504    self.ExecuteJavascript(js_code, tab_index)
505    success = self.WaitUntil(
506        lambda: bool(re.search('send:', self.GetDOMValue(
507            'document.getElementById("log").textContent', tab_index))))
508    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
509
510  def _VerifyDynamicLibraryOpen(self, tab_index, name, url):
511    """Verify Dynamic Library Open Example.
512
513    Args:
514      tab_index: Tab index integer that the example is on.
515      name: A string name of the example.
516      url: A string url of the example.
517    """
518    # Check if example is loaded.
519    success = self._VerifyElementPresent('log', 'Eightball loaded!',
520        tab_index, msg='Example %s failed. URL: %s' % (name, url))
521
522    # Simulate clicking on ASK button and check answer log for desired answer.
523    js_code = """
524      document.getElementsByTagName('input')[1].click();
525      window.domAutomationController.send('done');
526    """
527    self.ExecuteJavascript(js_code, tab_index)
528    def _CheckAnswerLog():
529      return bool(re.search(r'NO|YES|42|MAYBE NOT|DEFINITELY|'
530        'ASK ME TOMORROW|MAYBE|PARTLY CLOUDY',
531        self.GetDOMValue('document.getElementById("log").innerHTML',
532        tab_index)))
533
534    success = self.WaitUntil(_CheckAnswerLog)
535    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
536
537  def _VerifyLoadProgressExample(self, tab_index, name, url):
538    """Verify Dynamic Library Open Example.
539
540    Args:
541      tab_index: Tab index integer that the example is on.
542      name: A string name of the example.
543      url: A string url of the example.
544    """
545    # Check if example loads and displays loading progress.
546    success = self.WaitUntil(
547        lambda: self.GetDOMValue(
548        'document.getElementById("statusField").innerHTML', tab_index),
549        timeout=150, expect_retval='SUCCESS')
550    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
551
552    def _CheckLoadProgressStatus():
553      return re.search(
554          r'(loadstart).+(progress:).+(load).+(loadend).+(lastError:)',
555          self.GetDOMValue(
556          'document.getElementById("log").innerHTML', tab_index))
557    success = self.WaitUntil(_CheckLoadProgressStatus)
558    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
559
560  def _VerifyPiGeneratorExample(self, tab_index, name, url):
561    """Verify Pi Generator Example.
562
563    Args:
564      tab_index: Tab index integer that the example is on.
565      name: A string name of the example.
566      url: A string url of the example.
567    """
568    success = self.WaitUntil(
569        lambda: self.GetDOMValue('document.getElementById("pi").value',
570            tab_index)[0:3],
571        expect_retval='3.1')
572    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
573
574  def _VerifySineSynthExample(self, tab_index, name, url):
575    """Verify Sine Wave Synthesizer Example.
576
577    Args:
578      tab_index: Tab index integer that the example is on.
579      name: A string name of the example.
580      url: A string url of the example.
581    """
582    success = self.WaitUntil(
583        lambda: self.GetDOMValue(
584                    'document.getElementById("frequency_field").value',
585                    tab_index), timeout=150, expect_retval='440')
586    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
587    self.ExecuteJavascript(
588        'document.body.getElementsByTagName("button")[0].click();'
589        'window.domAutomationController.send("done")',
590        tab_index)
591
592  def _VerifyGetURLExample(self, tab_index, name, url):
593    """Verify GetURL Example.
594
595    Args:
596      tab_index: Tab index integer that the example is on.
597      name: A string name of the example.
598      url: A string url of the example.
599    """
600    success = self.WaitUntil(
601        lambda: self.GetDOMValue(
602                    'document.getElementById("statusField").innerHTML',
603                    tab_index), timeout=150, expect_retval='SUCCESS')
604    self.assertTrue(success, msg='Example %s failed. URL: %s' % (name, url))
605    self.ExecuteJavascript(
606        'document.getElementById("button").click();'
607        'window.domAutomationController.send("done")',
608        tab_index)
609    success = self._VerifyElementPresent('general_output', 'test passed',
610        tab_index, msg='Example %s failed. URL: %s' % (name, url))
611
612  def _CheckForCrashes(self):
613    """Check for any browser/tab crashes and hangs."""
614    self.assertTrue(self.GetBrowserWindowCount(),
615                    msg='Browser crashed, no window is open.')
616
617    info = self.GetBrowserInfo()
618    breakpad_folder = info['properties']['DIR_CRASH_DUMPS']
619    old_dmp_files = glob.glob(os.path.join(breakpad_folder, '*.dmp'))
620
621    # Verify there're no crash dump files.
622    for dmp_file in glob.glob(os.path.join(breakpad_folder, '*.dmp')):
623      self.assertTrue(dmp_file in old_dmp_files,
624                      msg='Crash dump %s found' % dmp_file)
625
626    # Check for any crashed tabs.
627    tabs = info['windows'][0]['tabs']
628    for tab in tabs:
629      if tab['url'] != 'about:blank':
630        if not self.GetDOMValue('document.body.innerHTML', tab['index']):
631          self.fail(msg='Tab crashed on %s' % tab['url'])
632
633  def _GetPlatformArchitecture(self):
634    """Get platform architecture.
635
636    Returns:
637      A string representing the platform architecture.
638    """
639    if pyauto.PyUITest.IsWin():
640      if os.environ['PROGRAMFILES'] == 'C:\\Program Files (x86)':
641        return '64bit'
642      else:
643        return '32bit'
644    elif pyauto.PyUITest.IsMac() or pyauto.PyUITest.IsLinux():
645      if platform.machine() == 'x86_64':
646        return '64bit'
647      else:
648        return '32bit'
649    return '32bit'
650
651  def _HasPathInTree(self, pattern, is_file, root=os.curdir):
652    """Recursively checks if a file/directory matching a pattern exists.
653
654    Args:
655      pattern: Pattern of file or directory name.
656      is_file: True if looking for file, or False if looking for directory.
657      root: Directory to start looking.
658
659    Returns:
660      True, if root contains the directory name pattern, or
661      False otherwise.
662    """
663    for path, dirs, files in os.walk(os.path.abspath(root)):
664      if is_file:
665        if len(fnmatch.filter(files, pattern)):
666          return True
667      else:
668        if len(fnmatch.filter(dirs, pattern)):
669          return True
670    return False
671
672  def _HasAllSystemRequirements(self):
673    """Verify NaCl SDK installation system requirements.
674
675    Returns:
676        True, if system passed requirements, or
677        False otherwise.
678    """
679    # Check python version.
680    if sys.version_info[0:2] < (2, 6):
681      return False
682
683    # Check OS requirements.
684    if pyauto.PyUITest.IsMac():
685      mac_min_version = version.StrictVersion('10.6')
686      mac_version = version.StrictVersion(platform.mac_ver()[0])
687      if mac_version < mac_min_version:
688        return False
689    elif pyauto.PyUITest.IsWin():
690      if not (self.IsWin7() or self.IsWinVista() or self.IsWinXP()):
691        return False
692    elif pyauto.PyUITest.IsLinux():
693      pass  # TODO(chrisphan): Check Lin requirements.
694    else:
695      return False
696
697    # Check for Chrome version compatibility.
698    # NaCl supports Chrome 10 and higher builds.
699    min_required_chrome_build = self._settings['min_required_chrome_build']
700    browser_info = self.GetBrowserInfo()
701    chrome_version = browser_info['properties']['ChromeVersion']
702    chrome_build = int(chrome_version.split('.')[0])
703    return chrome_build >= min_required_chrome_build
704
705  def _DownloadNaClSDK(self):
706    """Download NaCl SDK."""
707    self._temp_dir = tempfile.mkdtemp()
708    dl_file = urllib2.urlopen(self._settings['post_sdk_zip'])
709    file_path = os.path.join(self._temp_dir, 'nacl_sdk.zip')
710
711    try:
712      f = open(file_path, 'wb')
713      f.write(dl_file.read())
714    except IOError:
715      self.fail(msg='Cannot open %s.' % file_path)
716    finally:
717      f.close()
718
719  def _ExtractNaClSDK(self):
720    """Extract NaCl SDK."""
721    source_file = os.path.join(self._temp_dir, 'nacl_sdk.zip')
722    if zipfile.is_zipfile(source_file):
723      zip = zipfile.ZipFile(source_file, 'r')
724      zip.extractall(self._extracted_sdk_path)
725    else:
726      self.fail(msg='%s is not a valid zip file' % source_file)
727
728  def _IsURLAlive(self, url):
729    """Test if URL is alive."""
730    try:
731      urllib2.urlopen(url)
732    except:
733      return False
734    return True
735
736  def _CloseHTTPServer(self, proc=None):
737    """Close HTTP server.
738
739    Args:
740      proc: Process that opened the HTTP server.
741      proc is None when there is no pointer to HTTP server process.
742    """
743    if not self._IsURLAlive('http://localhost:5103'):
744      return
745    response = urllib2.urlopen('http://localhost:5103')
746    html = response.read()
747    if not 'Native Client' in html:
748      self.fail(msg='Port 5103 is in use.')
749
750    urllib2.urlopen('http://localhost:5103?quit=1')
751    success = self.WaitUntil(
752        lambda: self._IsURLAlive('http://localhost:5103'),
753        retry_sleep=1, expect_retval=False)
754    if not success:
755      if not proc:
756        self.fail(msg='Failed to close HTTP server.')
757      else:
758        if proc.poll() == None:
759          try:
760            proc.kill()
761          except:
762            self.fail(msg='Failed to close HTTP server.')
763
764  def _SearchNaClSDKFile(self, search_list):
765    """Search NaCl SDK file for example files and directories in Windows.
766
767    Args:
768      search_list: A list of strings, representing file and
769                   directory names for which to search.
770    """
771    missing_items = []
772    for name in search_list:
773      is_file = name.find('/') < 0
774      if not is_file:
775        name = name.replace('/', '')
776      if not self._HasPathInTree(name, is_file, self._extracted_sdk_path):
777        missing_items.append(name)
778    self.assertEqual(len(missing_items), 0,
779                     msg='Missing files or directories: %s' %
780                         ', '.join(map(str, missing_items)))
781
782  def ExtraChromeFlags(self):
783    """Ensures Nacl is enabled.
784
785    Returns:
786      A list of extra flags to pass to Chrome when it is launched.
787    """
788    extra_chrome_flags = [
789        '--enable-nacl',
790        '--enable-nacl-exception-handling',
791        '--nacl-gdb',
792      ]
793    return pyauto.PyUITest.ExtraChromeFlags(self) + extra_chrome_flags
794
795if __name__ == '__main__':
796  pyauto_functional.Main()
797