diff options
| author | Philipp Hagemeister <phihag@phihag.de> | 2014-07-18 14:20:34 +0200 | 
|---|---|---|
| committer | Philipp Hagemeister <phihag@phihag.de> | 2014-07-19 23:05:07 +0200 | 
| commit | 0cb2056304178ae8944e84c5bc72f96102291a12 (patch) | |
| tree | 440fb20b2907a34f89cd9a4f4cbf04313b7d572b | |
| parent | 5425626790a46f9b5bdecf4e33bb254c4c2423ea (diff) | |
[swfinterp] Start working on basic tests
| -rw-r--r-- | test/swftests/.gitignore | 1 | ||||
| -rw-r--r-- | test/swftests/LocalVars.as | 13 | ||||
| -rw-r--r-- | test/test_swfinterp.py | 73 | ||||
| -rw-r--r-- | youtube_dl/swfinterp.py | 56 | 
4 files changed, 126 insertions, 17 deletions
diff --git a/test/swftests/.gitignore b/test/swftests/.gitignore new file mode 100644 index 000000000..da97ff7ca --- /dev/null +++ b/test/swftests/.gitignore @@ -0,0 +1 @@ +*.swf diff --git a/test/swftests/LocalVars.as b/test/swftests/LocalVars.as new file mode 100644 index 000000000..b2911a9f3 --- /dev/null +++ b/test/swftests/LocalVars.as @@ -0,0 +1,13 @@ +// input: [1, 2] +// output: 3 + +package { +public class LocalVars { +    public static function main(a:int, b:int):int{ +        var c:int = a + b + b; +        var d:int = c - b; +        var e:int = d; +        return e; +    } +} +} diff --git a/test/test_swfinterp.py b/test/test_swfinterp.py new file mode 100644 index 000000000..98a14a006 --- /dev/null +++ b/test/test_swfinterp.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +# Allow direct execution +import os +import sys +import unittest +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +import io +import json +import re +import subprocess + +from youtube_dl.swfinterp import SWFInterpreter + + +TEST_DIR = os.path.join( +    os.path.dirname(os.path.abspath(__file__)), 'swftests') + + +class TestSWFInterpreter(unittest.TestCase): +    pass + + +for testfile in os.listdir(TEST_DIR): +    m = re.match(r'^(.*)\.(as)$', testfile) +    if not m: +        continue +    test_id = m.group(1) + +    def test_func(self): +        as_file = os.path.join(TEST_DIR, testfile) +        swf_file = os.path.join(TEST_DIR, test_id + '.swf') +        if ((not os.path.exists(swf_file)) +                or os.path.getmtime(swf_file) < os.path.getmtime(as_file)): +            # Recompile +            try: +                subprocess.check_call(['mxmlc', '--output', swf_file, as_file]) +            except OSError as ose: +                if ose.errno == errno.ENOENT: +                    print('mxmlc not found! Skipping test.') +                    return +                raise + +        with open(swf_file, 'rb') as swf_f: +            swf_content = swf_f.read() +        swfi = SWFInterpreter(swf_content) + +        with io.open(as_file, 'r', encoding='utf-8') as as_f: +            as_content = as_f.read() + +        def _find_spec(key): +            m = re.search( +                r'(?m)^//\s*%s:\s*(.*?)\n' % re.escape(key), as_content) +            if not m: +                raise ValueError('Cannot find %s in %s' % (key, testfile)) +            return json.loads(m.group(1)) + +        input_args = _find_spec('input') +        output = _find_spec('output') + +        swf_class = swfi.extract_class(test_id) +        func = swfi.extract_function(swf_class, 'main') +        res = func(input_args) +        self.assertEqual(res, output) + +    test_func.__name__ = str('test_swf_' + test_id) +    setattr(TestSWFInterpreter, test_func.__name__, test_func) + + +if __name__ == '__main__': +    unittest.main() diff --git a/youtube_dl/swfinterp.py b/youtube_dl/swfinterp.py index 1cd292138..49fade364 100644 --- a/youtube_dl/swfinterp.py +++ b/youtube_dl/swfinterp.py @@ -8,8 +8,22 @@ import zlib  from .utils import ExtractorError -def _extract_tags(content): -    pos = 0 +def _extract_tags(file_contents): +    if file_contents[1:3] != b'WS': +        raise ExtractorError( +            'Not an SWF file; header is %r' % file_contents[:3]) +    if file_contents[:1] == b'C': +        content = zlib.decompress(file_contents[8:]) +    else: +        raise NotImplementedError( +            'Unsupported compression format %r' % +            file_contents[:1]) + +    # Determine number of bits in framesize rectangle +    framesize_nbits = struct.unpack('!B', content[:1])[0] >> 3 +    framesize_len = (5 + 4 * framesize_nbits + 7) // 8 + +    pos = framesize_len + 2 + 2      while pos < len(content):          header16 = struct.unpack('<H', content[pos:pos + 2])[0]          pos += 2 @@ -18,7 +32,9 @@ def _extract_tags(content):          if tag_len == 0x3f:              tag_len = struct.unpack('<I', content[pos:pos + 4])[0]              pos += 4 -        assert pos + tag_len <= len(content) +        assert pos + tag_len <= len(content), \ +            ('Tag %d ends at %d+%d - that\'s longer than the file (%d)' +                % (tag_code, pos, tag_len, len(content)))          yield (tag_code, content[pos:pos + tag_len])          pos += tag_len @@ -88,8 +104,7 @@ def _read_string(reader):  def _read_bytes(count, reader): -    if reader is None: -        reader = code_reader +    assert count >= 0      resb = reader.read(count)      assert len(resb) == count      return resb @@ -103,18 +118,8 @@ def _read_byte(reader):  class SWFInterpreter(object):      def __init__(self, file_contents): -        if file_contents[1:3] != b'WS': -            raise ExtractorError( -                'Not an SWF file; header is %r' % file_contents[:3]) -        if file_contents[:1] == b'C': -            content = zlib.decompress(file_contents[8:]) -        else: -            raise NotImplementedError( -                'Unsupported compression format %r' % -                file_contents[:1]) -          code_tag = next(tag -                        for tag_code, tag in _extract_tags(content) +                        for tag_code, tag in _extract_tags(file_contents)                          if tag_code == 82)          p = code_tag.index(b'\0', 4) + 1          code_reader = io.BytesIO(code_tag[p:]) @@ -139,7 +144,7 @@ class SWFInterpreter(object):          for _c in range(1, uint_count):              u32()          double_count = u30() -        read_bytes((double_count - 1) * 8) +        read_bytes(max(0, (double_count - 1)) * 8)          string_count = u30()          constant_strings = ['']          for _c in range(1, string_count): @@ -349,6 +354,9 @@ class SWFInterpreter(object):                  elif opcode == 36:  # pushbyte                      v = _read_byte(coder)                      stack.append(v) +                elif opcode == 42:  # dup +                    value = stack[-1] +                    stack.append(value)                  elif opcode == 44:  # pushstring                      idx = u30()                      stack.append(constant_strings[idx]) @@ -468,10 +476,24 @@ class SWFInterpreter(object):                          obj = stack.pop()                          assert isinstance(obj, list)                          stack.append(obj[idx]) +                elif opcode == 115:  # convert_ +                    value = stack.pop() +                    intvalue = int(value) +                    stack.append(intvalue)                  elif opcode == 128:  # coerce                      u30()                  elif opcode == 133:  # coerce_s                      assert isinstance(stack[-1], (type(None), compat_str)) +                elif opcode == 160:  # add +                    value2 = stack.pop() +                    value1 = stack.pop() +                    res = value1 + value2 +                    stack.append(res) +                elif opcode == 161:  # subtract +                    value2 = stack.pop() +                    value1 = stack.pop() +                    res = value1 - value2 +                    stack.append(res)                  elif opcode == 164:  # modulo                      value2 = stack.pop()                      value1 = stack.pop()  | 
