Changeset 83475 in webkit


Ignore:
Timestamp:
Apr 11, 2011 12:17:57 PM (13 years ago)
Author:
dpranke@chromium.org
Message:

2011-04-11 Dirk Pranke <dpranke@chromium.org>

Reviewed by Tony Chang.

new-run-webkit-tests: implement support for audio, take two
https://bugs.webkit.org/show_bug.cgi?id=58195

Attempt to re-land the fix for bug 58101 (which was initially
landed in r83330, but rolled out). This is the same patch but
fixes crashes in Port.diff_image() caused by the change for
empty image files being None instead of .

  • Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py:
  • Scripts/webkitpy/layout_tests/layout_package/single_test_runner.py:
  • Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
  • Scripts/webkitpy/layout_tests/layout_package/test_failures.py:
  • Scripts/webkitpy/layout_tests/layout_package/test_result_writer.py:
  • Scripts/webkitpy/layout_tests/port/base.py:
  • Scripts/webkitpy/layout_tests/port/chromium.py:
  • Scripts/webkitpy/layout_tests/port/dryrun.py:
  • Scripts/webkitpy/layout_tests/port/mock_drt.py:
  • Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py:
  • Scripts/webkitpy/layout_tests/port/test.py:
  • Scripts/webkitpy/layout_tests/port/webkit.py:
Location:
trunk/Tools
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r83466 r83475  
     12011-04-11  Dirk Pranke  <dpranke@chromium.org>
     2
     3        Reviewed by Tony Chang.
     4
     5        new-run-webkit-tests: implement support for audio, take two
     6        https://bugs.webkit.org/show_bug.cgi?id=58195
     7
     8        Attempt to re-land the fix for bug 58101 (which was initially
     9        landed in r83330, but rolled out). This is the same patch but
     10        fixes crashes in Port.diff_image() caused by the change for
     11        empty image files being None instead of ''.
     12
     13        * Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py:
     14        * Scripts/webkitpy/layout_tests/layout_package/single_test_runner.py:
     15        * Scripts/webkitpy/layout_tests/layout_package/test_expectations.py:
     16        * Scripts/webkitpy/layout_tests/layout_package/test_failures.py:
     17        * Scripts/webkitpy/layout_tests/layout_package/test_result_writer.py:
     18        * Scripts/webkitpy/layout_tests/port/base.py:
     19        * Scripts/webkitpy/layout_tests/port/chromium.py:
     20        * Scripts/webkitpy/layout_tests/port/dryrun.py:
     21        * Scripts/webkitpy/layout_tests/port/mock_drt.py:
     22        * Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py:
     23        * Scripts/webkitpy/layout_tests/port/test.py:
     24        * Scripts/webkitpy/layout_tests/port/webkit.py:
     25
    1262011-04-11  Mario Sanchez Prada  <msanchez@igalia.com>
    227
  • trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/json_layout_results_generator.py

    r83361 r83475  
    5151                       test_expectations.TEXT: "F",
    5252                       test_expectations.MISSING: "O",
     53                       test_expectations.AUDIO: "A",
    5354                       test_expectations.IMAGE_PLUS_TEXT: "Z"}
    5455
  • trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/single_test_runner.py

    r83361 r83475  
    8383            # 'foo-expected.txt', we should warn users. One test file must be used exclusively
    8484            # in either layout tests or reftests, but not in both.
    85             for suffix in ['.txt', '.checksum', '.png']:
     85            for suffix in ('.txt', '.checksum', '.png', '.wav'):
    8686                expected_filename = self._port.expected_filename(self._filename, suffix)
    8787                if fs.exists(expected_filename):
     
    9292        return base.DriverOutput(self._port.expected_text(self._filename),
    9393                                 self._port.expected_image(self._filename),
    94                                  self._port.expected_checksum(self._filename))
     94                                 self._port.expected_checksum(self._filename),
     95                                 self._port.expected_audio(self._filename))
    9596
    9697    def _should_fetch_expected_checksum(self):
     
    143144        self._save_baseline_data(driver_output.text, ".txt",
    144145                                 generate_new_baseline=self._options.new_baseline)
     146        if driver_output.audio:
     147            self._save_baseline_data(driver_output.audio, '.wav',
     148                                     generate_new_baseline=self._options.new_baseline)
    145149        if self._options.pixel_tests and driver_output.image_hash:
    146150            self._save_baseline_data(driver_output.image, ".png",
     
    217221
    218222        failures.extend(self._compare_text(driver_output.text, expected_driver_output.text))
     223        failures.extend(self._compare_audio(driver_output.audio, expected_driver_output.audio))
    219224        if self._options.pixel_tests:
    220225            failures.extend(self._compare_image(driver_output, expected_driver_output))
     
    223228    def _compare_text(self, actual_text, expected_text):
    224229        failures = []
    225         if self._port.compare_text(self._get_normalized_output_text(actual_text),
    226                                    # Assuming expected_text is already normalized.
    227                                    expected_text):
    228             if expected_text == '':
    229                 failures.append(test_failures.FailureMissingResult())
    230             else:
    231                 failures.append(test_failures.FailureTextMismatch())
     230        if (expected_text and actual_text and
     231            # Assuming expected_text is already normalized.
     232            self._port.compare_text(self._get_normalized_output_text(actual_text), expected_text)):
     233            failures.append(test_failures.FailureTextMismatch())
     234        elif actual_text and not expected_text:
     235            failures.append(test_failures.FailureMissingResult())
     236        return failures
     237
     238    def _compare_audio(self, actual_audio, expected_audio):
     239        failures = []
     240        if (expected_audio and actual_audio and
     241            self._port.compare_audio(actual_audio, expected_audio)):
     242            failures.append(test_failures.FailureAudioMismatch())
     243        elif actual_audio and not expected_audio:
     244            failures.append(test_failures.FailureMissingAudio())
    232245        return failures
    233246
  • trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/test_expectations.py

    r83361 r83475  
    4242
    4343# Test expectation and modifier constants.
    44 (PASS, FAIL, TEXT, IMAGE, IMAGE_PLUS_TEXT, TIMEOUT, CRASH, SKIP, WONTFIX,
    45  SLOW, REBASELINE, MISSING, FLAKY, NOW, NONE) = range(15)
     44(PASS, FAIL, TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO, TIMEOUT, CRASH, SKIP, WONTFIX,
     45 SLOW, REBASELINE, MISSING, FLAKY, NOW, NONE) = range(16)
    4646
    4747# Test expectation file update action constants
     
    121121                self._expected_failures.get_test_set(REBASELINE, TEXT) |
    122122                self._expected_failures.get_test_set(REBASELINE,
    123                                                      IMAGE_PLUS_TEXT))
     123                                                     IMAGE_PLUS_TEXT) |
     124                self._expected_failures.get_test_set(REBASELINE, AUDIO))
    124125
    125126    def get_options(self, test):
     
    245246    Notes:
    246247      -A test cannot be both SLOW and TIMEOUT
    247       -A test should only be one of IMAGE, TEXT, IMAGE+TEXT, or FAIL. FAIL is
    248        a migratory state that currently means either IMAGE, TEXT, or
    249        IMAGE+TEXT. Once we have finished migrating the expectations, we will
    250        change FAIL to have the meaning of IMAGE+TEXT and remove the IMAGE+TEXT
    251        identifier.
     248      -A test should only be one of IMAGE, TEXT, IMAGE+TEXT, AUDIO, or FAIL.
     249       FAIL is a legacy value that currently means either IMAGE,
     250       TEXT, or IMAGE+TEXT. Once we have finished migrating the expectations,
     251       we should change FAIL to have the meaning of IMAGE+TEXT and remove the
     252       IMAGE+TEXT identifier.
    252253      -A test can be included twice, but not via the same path.
    253254      -If a test is included twice, then the more precise path wins.
     
    260261                    'image': IMAGE,
    261262                    'image+text': IMAGE_PLUS_TEXT,
     263                    'audio': AUDIO,
    262264                    'timeout': TIMEOUT,
    263265                    'crash': CRASH,
     
    272274                                IMAGE_PLUS_TEXT: ('image and text mismatch',
    273275                                                  'image and text mismatch'),
     276                                AUDIO: ('audio mismatch', 'audio mismatch'),
    274277                                CRASH: ('DumpRenderTree crash',
    275278                                        'DumpRenderTree crashes'),
     
    279282
    280283    EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, MISSING, IMAGE_PLUS_TEXT,
    281        TEXT, IMAGE, FAIL, SKIP)
     284       TEXT, IMAGE, AUDIO, FAIL, SKIP)
    282285
    283286    BUILD_TYPES = ('debug', 'release')
  • trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/test_failures.py

    r83361 r83475  
    5555    elif (FailureMissingResult in failure_types or
    5656          FailureMissingImage in failure_types or
    57           FailureMissingImageHash in failure_types):
     57          FailureMissingImageHash in failure_types or
     58          FailureMissingAudio in failure_types):
    5859        return test_expectations.MISSING
    5960    else:
     
    6364        is_reftest_failure = (FailureReftestMismatch in failure_types or
    6465                              FailureReftestMismatchDidNotOccur in failure_types)
     66        is_audio_failure = (FailureAudioMismatch in failure_types)
    6567        if is_text_failure and is_image_failure:
    6668            return test_expectations.IMAGE_PLUS_TEXT
     
    6971        elif is_image_failure or is_reftest_failure:
    7072            return test_expectations.IMAGE
     73        elif is_audio_failure:
     74            return test_expectations.AUDIO
    7175        else:
    7276            raise ValueError("unclassifiable set of failures: "
     
    332336
    333337
     338class FailureMissingAudio(ComparisonTestFailure):
     339    """Actual result image was missing."""
     340    OUT_FILENAMES = ("-actual.wav",)
     341
     342    @staticmethod
     343    def message():
     344        return "No expected audio found"
     345
     346    def result_html_output(self, filename):
     347        return ("<strong>%s</strong>" % self.message() +
     348                self.output_links(filename, self.OUT_FILENAMES))
     349
     350
     351class FailureAudioMismatch(ComparisonTestFailure):
     352    """Audio files didn't match."""
     353    OUT_FILENAMES = ("-actual.wav", "-expected.wav")
     354
     355    @staticmethod
     356    def message():
     357        return "Audio mismatch"
     358
     359
    334360# Convenient collection of all failure classes for anything that might
    335361# need to enumerate over them all.
  • trunk/Tools/Scripts/webkitpy/layout_tests/layout_package/test_result_writer.py

    r83361 r83475  
    6464                checksums_mismatch_but_images_are_same = True
    6565                imagehash_mismatch_failure = failure
     66        elif isinstance(failure, (test_failures.FailureAudioMismatch,
     67                                  test_failures.FailureMissingAudio)):
     68            writer.write_audio_files(driver_output.audio, expected_driver_output.audio)
    6669        elif isinstance(failure, test_failures.FailureCrash):
    6770            if failure.reference_filename:
     
    188191        fs.write_binary_file(pretty_patch_filename, pretty_patch)
    189192
     193    def write_audio_files(self, actual_audio, expected_audio):
     194        self.write_output_files('.wav', actual_audio, expected_audio)
     195
    190196    def write_image_files(self, actual_image, expected_image):
    191197        self.write_output_files('.png', actual_image, expected_image)
  • trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py

    r83361 r83475  
    207207        return expected_text != actual_text
    208208
     209    def compare_audio(self, expected_audio, actual_audio):
     210        """Return whether the two audio files are *not* equal."""
     211        return expected_audio != actual_audio
     212
    209213    def diff_image(self, expected_contents, actual_contents,
    210214                   diff_filename=None, tolerance=0):
     
    352356        return self._filesystem.read_binary_file(path)
    353357
     358    def expected_audio(self, test):
     359        path = self.expected_filename(test, '.wav')
     360        if not self.path_exists(path):
     361            return None
     362        return self._filesystem.read_binary_file(path)
     363
    354364    def expected_text(self, test):
    355         """Returns the text output we expect the test to produce.
     365        """Returns the text output we expect the test to produce, or None
     366        if we don't expect there to be any text output.
    356367        End-of-line characters are normalized to '\n'."""
    357368        # FIXME: DRT output is actually utf-8, but since we don't decode the
     
    360371        path = self.expected_filename(test, '.txt')
    361372        if not self.path_exists(path):
    362             return ''
     373            return None
    363374        text = self._filesystem.read_binary_file(path)
    364375        return text.replace("\r\n", "\n")
     
    868879    """Groups information about a output from driver for easy passing of data."""
    869880
    870     def __init__(self, text, image, image_hash,
    871                  crash=False, test_time=None, timeout=False, error=''):
     881    def __init__(self, text, image, image_hash, audio,
     882                 crash=False, test_time=0, timeout=False, error=''):
    872883        """Initializes a TestOutput object.
    873884
     
    876887          image: an image output
    877888          image_hash: a string containing the checksum of the image
     889          audio: contents of an audio stream, if any (in WAV format)
    878890          crash: a boolean indicating whether the driver crashed on the test
    879           test_time: a time which the test has taken
     891          test_time: the time the test took to execute
    880892          timeout: a boolean indicating whehter the test timed out
    881893          error: any unexpected or additional (or error) text output
     
    884896        self.image = image
    885897        self.image_hash = image_hash
     898        self.audio = audio
    886899        self.crash = crash
    887900        self.test_time = test_time
  • trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py

    r83398 r83475  
    137137    def diff_image(self, expected_contents, actual_contents,
    138138                   diff_filename=None):
    139         executable = self._path_to_image_diff()
     139        # FIXME: need unit tests for this.
     140        if not actual_contents and not expected_contents:
     141            return False
     142        if not actual_contents or not expected_contents:
     143            return True
    140144
    141145        tempdir = self._filesystem.mkdtemp()
     
    145149        self._filesystem.write_binary_file(actual_filename, actual_contents)
    146150
     151        executable = self._path_to_image_diff()
    147152        if diff_filename:
    148153            cmd = [executable, '--diff', expected_filename,
     
    427432            return self._port._filesystem.read_binary_file(png_path)
    428433        else:
    429             return ''
     434            return None
    430435
    431436    def _output_image_with_retry(self):
     
    502507            (line, crash) = self._write_command_and_read_line(input=None)
    503508
     509        # FIXME: Add support for audio when we're ready.
     510
    504511        run_time = time.time() - start_time
    505512        output_image = self._output_image_with_retry()
    506         assert output_image is not None
    507         return base.DriverOutput(''.join(output), output_image, actual_checksum,
    508                                  crash, run_time, timeout, ''.join(error))
     513        text = ''.join(output)
     514        if not text:
     515            text = None
     516
     517        return base.DriverOutput(text, output_image, actual_checksum, audio=None,
     518            crash=crash, test_time=run_time, timeout=timeout, error=''.join(error))
    509519
    510520    def stop(self):
  • trunk/Tools/Scripts/webkitpy/layout_tests/port/dryrun.py

    r83361 r83475  
    120120        start_time = time.time()
    121121        fs = self._port._filesystem
    122         if fs.exists(self._port.reftest_expected_filename(driver_input.filename)) or \
    123             fs.exists(self._port.reftest_expected_mismatch_filename(driver_input.filename)):
    124             text_output = 'test-text'
     122        if (fs.exists(self._port.reftest_expected_filename(driver_input.filename)) or
     123            fs.exists(self._port.reftest_expected_mismatch_filename(driver_input.filename)) or
     124            driver_input.filename.endswith('-expected.html')):
     125            text = 'test-text'
    125126            image = 'test-image'
    126             hash = 'test-checksum'
    127         elif driver_input.filename.endswith('-expected.html'):
    128             text_output = 'test-text'
    129             image = 'test-image'
    130             hash = 'test-checksum'
     127            checksum = 'test-checksum'
     128            audio = None
    131129        elif driver_input.filename.endswith('-expected-mismatch.html'):
    132             text_output = 'test-text-mismatch'
     130            text = 'test-text-mismatch'
    133131            image = 'test-image-mismatch'
    134             hash = 'test-checksum-mismatch'
    135         elif driver_input.image_hash is not None:
    136             text_output = self._port.expected_text(driver_input.filename)
     132            checksum = 'test-checksum-mismatch'
     133            audio = None
     134        else:
     135            text = self._port.expected_text(driver_input.filename)
    137136            image = self._port.expected_image(driver_input.filename)
    138             hash = self._port.expected_checksum(driver_input.filename)
    139         else:
    140             text_output = self._port.expected_text(driver_input.filename)
    141             image = None
    142             hash = None
    143         return base.DriverOutput(text_output, image, hash, False,
    144                                  time.time() - start_time, False, '')
     137            checksum = self._port.expected_checksum(driver_input.filename)
     138            audio = self._port.expected_audio(driver_input.filename)
     139        return base.DriverOutput(text, image, checksum, audio, crash=False,
     140            test_time=time.time() - start_time, timeout=False, error='')
    145141
    146142    def start(self):
  • trunk/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py

    r83361 r83475  
    3333"""
    3434
     35import base64
    3536import logging
    3637import optparse
     
    207208
    208209        actual_text = port.expected_text(test_path)
     210        actual_audio = port.expected_audio(test_path)
    209211        if self._options.pixel_tests and test_input.checksum:
    210212            actual_checksum = port.expected_checksum(test_path)
    211213            actual_image = port.expected_image(test_path)
    212214
    213         self._stdout.write('Content-Type: text/plain\n')
    214 
    215         # FIXME: Note that we don't ensure there is a trailing newline!
    216         # This mirrors actual (Mac) DRT behavior but is a bug.
    217         self._stdout.write(actual_text)
     215        if actual_audio:
     216            self._stdout.write('Content-Type: audio/wav\n')
     217            self._stdout.write('Content-Transfer-Encoding: base64\n')
     218            output = base64.b64encode(actual_audio)
     219            self._stdout.write('Content-Length: %s\n' % len(output))
     220            self._stdout.write(output)
     221        else:
     222            self._stdout.write('Content-Type: text/plain\n')
     223            # FIXME: Note that we don't ensure there is a trailing newline!
     224            # This mirrors actual (Mac) DRT behavior but is a bug.
     225            self._stdout.write(actual_text)
     226
    218227        self._stdout.write('#EOF\n')
    219228
     
    224233            if actual_checksum != test_input.checksum:
    225234                self._stdout.write('Content-Type: image/png\n')
    226                 self._stdout.write('Content-Length: %s\n\n' % len(actual_image))
     235                self._stdout.write('Content-Length: %s\n' % len(actual_image))
    227236                self._stdout.write(actual_image)
    228237        self._stdout.write('#EOF\n')
  • trunk/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py

    r83361 r83475  
    201201                        'ExpectedHash: wrong-checksum\n',
    202202                        'Content-Type: image/png\n',
    203                         'Content-Length: 13\n\n',
     203                        'Content-Length: 13\n',
    204204                        'checksum\x8a-png',
    205205                        '#EOF\n'])
  • trunk/Tools/Scripts/webkitpy/layout_tests/port/test.py

    r83361 r83475  
    3131from __future__ import with_statement
    3232
     33import base64
    3334import time
    3435
     
    6768        self.expected_image = self.actual_image
    6869
     70        self.actual_audio = None
     71        self.expected_audio = None
    6972
    7073# This is an in-memory list of tests, what we want them to produce, and
     
    112115              actual_checksum='image_checksum_fail-checksum',
    113116              actual_image='image_checksum_fail-png')
     117    tests.add('failures/expected/audio.html',
     118              actual_audio=base64.b64encode('audio_fail-wav'), expected_audio='audio-wav',
     119              actual_text=None, expected_text=None,
     120              actual_image=None, expected_image=None,
     121              actual_checksum=None, expected_checksum=None)
    114122    tests.add('failures/expected/keyboard.html', keyboard=True)
    115123    tests.add('failures/expected/missing_check.html',
     
    117125              expected_image=None)
    118126    tests.add('failures/expected/missing_image.html', expected_image=None)
     127    tests.add('failures/expected/missing_audio.html', expected_audio=None,
     128              actual_text=None, expected_text=None,
     129              actual_image=None, expected_image=None,
     130              actual_checksum=None, expected_checksum=None)
    119131    tests.add('failures/expected/missing_text.html', expected_text=None)
    120132    tests.add('failures/expected/newlines_leading.html',
     
    135147    tests.add('passes/error.html', error='stuff going to stderr')
    136148    tests.add('passes/image.html')
     149    tests.add('passes/audio.html',
     150              actual_audio=base64.b64encode('audio-wav'), expected_audio='audio-wav',
     151              actual_text=None, expected_text=None,
     152              actual_image=None, expected_image=None,
     153              actual_checksum=None, expected_checksum=None)
    137154    tests.add('passes/platform_image.html')
    138155    tests.add('passes/checksum_in_image.html',
     
    185202        if test.is_reftest:
    186203            continue
     204        if test.actual_audio:
     205            add_file(files, test, '-expected.wav', test.expected_audio)
     206            continue
     207
    187208        add_file(files, test, '-expected.txt', test.expected_text)
    188209        add_file(files, test, '-expected.checksum', test.expected_checksum)
    189210        add_file(files, test, '-expected.png', test.expected_image)
     211
    190212
    191213    # Add the test_expectations file.
     
    195217// This one actually passes because the checksums will match.
    196218WONTFIX : failures/expected/image.html = PASS
     219WONTFIX : failures/expected/audio.html = AUDIO
    197220WONTFIX : failures/expected/image_checksum.html = IMAGE
    198221WONTFIX : failures/expected/mismatch.html = IMAGE
    199222WONTFIX : failures/expected/missing_check.html = MISSING PASS
    200223WONTFIX : failures/expected/missing_image.html = MISSING PASS
     224WONTFIX : failures/expected/missing_audio.html = MISSING PASS
    201225WONTFIX : failures/expected/missing_text.html = MISSING PASS
    202226WONTFIX : failures/expected/newlines_leading.html = TEXT
     
    413437        if test.hang:
    414438            time.sleep((float(test_input.timeout) * 4) / 1000.0)
     439
     440        audio = None
     441        if test.actual_audio:
     442            audio = base64.b64decode(test.actual_audio)
    415443        return base.DriverOutput(test.actual_text, test.actual_image,
    416                                  test.actual_checksum, test.crash,
    417                                  time.time() - start_time, test.timeout,
    418                                  test.error)
     444            test.actual_checksum, audio, crash=test.crash,
     445            test_time=time.time() - start_time, timeout=test.timeout, error=test.error)
    419446
    420447    def start(self):
  • trunk/Tools/Scripts/webkitpy/layout_tests/port/webkit.py

    r83361 r83475  
    3131"""WebKit implementations of the Port interface."""
    3232
    33 
     33import base64
    3434import logging
    3535import operator
     
    110110
    111111        # Handle the case where the test didn't actually generate an image.
    112         if not actual_contents:
     112        # FIXME: need unit tests for this.
     113        if not actual_contents and not expected_contents:
     114            return False
     115        if not actual_contents or not expected_contents:
    113116            return True
    114117
     
    407410        self._server_process.write(command)
    408411
    409         have_seen_content_type = False
     412        text = None
     413        image = None
    410414        actual_image_hash = None
    411         output = str()  # Use a byte array for output, even though it should be UTF-8.
    412         image = str()
    413 
    414         timeout = int(driver_input.timeout) / 1000.0
    415         deadline = time.time() + timeout
    416         line = self._server_process.read_line(timeout)
    417         while (not self._server_process.timed_out
    418                and not self._server_process.crashed
    419                and line.rstrip() != "#EOF"):
    420             if (line.startswith('Content-Type:') and not
    421                 have_seen_content_type):
    422                 have_seen_content_type = True
    423             else:
    424                 # Note: Text output from DumpRenderTree is always UTF-8.
    425                 # However, some tests (e.g. webarchives) spit out binary
    426                 # data instead of text.  So to make things simple, we
    427                 # always treat the output as binary.
    428                 output += line
    429             line = self._server_process.read_line(timeout)
    430             timeout = deadline - time.time()
    431 
    432         # Now read a second block of text for the optional image data
    433         remaining_length = -1
    434         HASH_HEADER = 'ActualHash: '
    435         LENGTH_HEADER = 'Content-Length: '
    436         line = self._server_process.read_line(timeout)
    437         while (not self._server_process.timed_out
    438                and not self._server_process.crashed
    439                and line.rstrip() != "#EOF"):
    440             if line.startswith(HASH_HEADER):
    441                 actual_image_hash = line[len(HASH_HEADER):].strip()
    442             elif line.startswith('Content-Type:'):
    443                 pass
    444             elif line.startswith(LENGTH_HEADER):
    445                 timeout = deadline - time.time()
    446                 content_length = int(line[len(LENGTH_HEADER):])
    447                 image = self._server_process.read(timeout, content_length)
    448             timeout = deadline - time.time()
    449             line = self._server_process.read_line(timeout)
     415        audio = None
     416        deadline = time.time() + int(driver_input.timeout) / 1000.0
     417
     418        # First block is either text or audio
     419        block = self._read_block(deadline)
     420        if block.content_type == 'audio/wav':
     421            audio = block.decoded_content
     422        else:
     423            text = block.decoded_content
     424
     425        # Now read an optional second block of image data
     426        block = self._read_block(deadline)
     427        if block.content and block.content_type == 'image/png':
     428            image = block.decoded_content
     429            actual_image_hash = block.content_hash
    450430
    451431        error_lines = self._server_process.error.splitlines()
     
    459439        # this reset in.
    460440        self._server_process.error = ""
    461         return base.DriverOutput(output, image, actual_image_hash,
    462                                  self._server_process.crashed,
    463                                  time.time() - start_time,
    464                                  self._server_process.timed_out,
    465                                  error)
     441        return base.DriverOutput(text, image, actual_image_hash, audio,
     442            crash=self._server_process.crashed, test_time=time.time() - start_time,
     443            timeout=self._server_process.timed_out, error=error)
     444
     445    def _read_block(self, deadline):
     446        LENGTH_HEADER = 'Content-Length: '
     447        HASH_HEADER = 'ActualHash: '
     448        TYPE_HEADER = 'Content-Type: '
     449        ENCODING_HEADER = 'Content-Transfer-Encoding: '
     450        content_type = None
     451        encoding = None
     452        content_hash = None
     453        content_length = None
     454
     455        # Content is treated as binary data even though the text output
     456        # is usually UTF-8.
     457        content = ''
     458        timeout = deadline - time.time()
     459        line = self._server_process.read_line(timeout)
     460        while (not self._server_process.timed_out
     461               and not self._server_process.crashed
     462               and line.rstrip() != "#EOF"):
     463            if line.startswith(TYPE_HEADER) and content_type is None:
     464                content_type = line.split()[1]
     465            elif line.startswith(ENCODING_HEADER) and encoding is None:
     466                encoding = line.split()[1]
     467            elif line.startswith(LENGTH_HEADER) and content_length is None:
     468                timeout = deadline - time.time()
     469                content_length = int(line[len(LENGTH_HEADER):])
     470                # FIXME: Technically there should probably be another blank
     471                # line here, but DRT doesn't write one.
     472                content = self._server_process.read(timeout, content_length)
     473            elif line.startswith(HASH_HEADER):
     474                content_hash = line.split()[1]
     475            else:
     476                content += line
     477            line = self._server_process.read_line(timeout)
     478            timeout = deadline - time.time()
     479        return ContentBlock(content_type, encoding, content_hash, content)
    466480
    467481    def stop(self):
     
    469483            self._server_process.stop()
    470484            self._server_process = None
     485
     486
     487class ContentBlock(object):
     488    def __init__(self, content_type, encoding, content_hash, content):
     489        self.content_type = content_type
     490        self.encoding = encoding
     491        self.content_hash = content_hash
     492        self.content = content
     493        if self.encoding == 'base64':
     494            self.decoded_content = base64.b64decode(content)
     495        else:
     496            self.decoded_content = content
Note: See TracChangeset for help on using the changeset viewer.