mirror of
				https://gitlab.com/ytdl-org/youtube-dl.git
				synced 2025-11-04 00:37:06 -05:00 
			
		
		
		
	[swfinterp] Start working on basic tests
This commit is contained in:
		
							
								
								
									
										1
									
								
								test/swftests/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/swftests/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					*.swf
 | 
				
			||||||
							
								
								
									
										13
									
								
								test/swftests/LocalVars.as
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								test/swftests/LocalVars.as
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								test/test_swfinterp.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								test/test_swfinterp.py
									
									
									
									
									
										Normal file
									
								
							@@ -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()
 | 
				
			||||||
@@ -8,8 +8,22 @@ import zlib
 | 
				
			|||||||
from .utils import ExtractorError
 | 
					from .utils import ExtractorError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _extract_tags(content):
 | 
					def _extract_tags(file_contents):
 | 
				
			||||||
    pos = 0
 | 
					    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):
 | 
					    while pos < len(content):
 | 
				
			||||||
        header16 = struct.unpack('<H', content[pos:pos + 2])[0]
 | 
					        header16 = struct.unpack('<H', content[pos:pos + 2])[0]
 | 
				
			||||||
        pos += 2
 | 
					        pos += 2
 | 
				
			||||||
@@ -18,7 +32,9 @@ def _extract_tags(content):
 | 
				
			|||||||
        if tag_len == 0x3f:
 | 
					        if tag_len == 0x3f:
 | 
				
			||||||
            tag_len = struct.unpack('<I', content[pos:pos + 4])[0]
 | 
					            tag_len = struct.unpack('<I', content[pos:pos + 4])[0]
 | 
				
			||||||
            pos += 4
 | 
					            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])
 | 
					        yield (tag_code, content[pos:pos + tag_len])
 | 
				
			||||||
        pos += tag_len
 | 
					        pos += tag_len
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -88,8 +104,7 @@ def _read_string(reader):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _read_bytes(count, reader):
 | 
					def _read_bytes(count, reader):
 | 
				
			||||||
    if reader is None:
 | 
					    assert count >= 0
 | 
				
			||||||
        reader = code_reader
 | 
					 | 
				
			||||||
    resb = reader.read(count)
 | 
					    resb = reader.read(count)
 | 
				
			||||||
    assert len(resb) == count
 | 
					    assert len(resb) == count
 | 
				
			||||||
    return resb
 | 
					    return resb
 | 
				
			||||||
@@ -103,18 +118,8 @@ def _read_byte(reader):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class SWFInterpreter(object):
 | 
					class SWFInterpreter(object):
 | 
				
			||||||
    def __init__(self, file_contents):
 | 
					    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
 | 
					        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)
 | 
					                        if tag_code == 82)
 | 
				
			||||||
        p = code_tag.index(b'\0', 4) + 1
 | 
					        p = code_tag.index(b'\0', 4) + 1
 | 
				
			||||||
        code_reader = io.BytesIO(code_tag[p:])
 | 
					        code_reader = io.BytesIO(code_tag[p:])
 | 
				
			||||||
@@ -139,7 +144,7 @@ class SWFInterpreter(object):
 | 
				
			|||||||
        for _c in range(1, uint_count):
 | 
					        for _c in range(1, uint_count):
 | 
				
			||||||
            u32()
 | 
					            u32()
 | 
				
			||||||
        double_count = u30()
 | 
					        double_count = u30()
 | 
				
			||||||
        read_bytes((double_count - 1) * 8)
 | 
					        read_bytes(max(0, (double_count - 1)) * 8)
 | 
				
			||||||
        string_count = u30()
 | 
					        string_count = u30()
 | 
				
			||||||
        constant_strings = ['']
 | 
					        constant_strings = ['']
 | 
				
			||||||
        for _c in range(1, string_count):
 | 
					        for _c in range(1, string_count):
 | 
				
			||||||
@@ -349,6 +354,9 @@ class SWFInterpreter(object):
 | 
				
			|||||||
                elif opcode == 36:  # pushbyte
 | 
					                elif opcode == 36:  # pushbyte
 | 
				
			||||||
                    v = _read_byte(coder)
 | 
					                    v = _read_byte(coder)
 | 
				
			||||||
                    stack.append(v)
 | 
					                    stack.append(v)
 | 
				
			||||||
 | 
					                elif opcode == 42:  # dup
 | 
				
			||||||
 | 
					                    value = stack[-1]
 | 
				
			||||||
 | 
					                    stack.append(value)
 | 
				
			||||||
                elif opcode == 44:  # pushstring
 | 
					                elif opcode == 44:  # pushstring
 | 
				
			||||||
                    idx = u30()
 | 
					                    idx = u30()
 | 
				
			||||||
                    stack.append(constant_strings[idx])
 | 
					                    stack.append(constant_strings[idx])
 | 
				
			||||||
@@ -468,10 +476,24 @@ class SWFInterpreter(object):
 | 
				
			|||||||
                        obj = stack.pop()
 | 
					                        obj = stack.pop()
 | 
				
			||||||
                        assert isinstance(obj, list)
 | 
					                        assert isinstance(obj, list)
 | 
				
			||||||
                        stack.append(obj[idx])
 | 
					                        stack.append(obj[idx])
 | 
				
			||||||
 | 
					                elif opcode == 115:  # convert_
 | 
				
			||||||
 | 
					                    value = stack.pop()
 | 
				
			||||||
 | 
					                    intvalue = int(value)
 | 
				
			||||||
 | 
					                    stack.append(intvalue)
 | 
				
			||||||
                elif opcode == 128:  # coerce
 | 
					                elif opcode == 128:  # coerce
 | 
				
			||||||
                    u30()
 | 
					                    u30()
 | 
				
			||||||
                elif opcode == 133:  # coerce_s
 | 
					                elif opcode == 133:  # coerce_s
 | 
				
			||||||
                    assert isinstance(stack[-1], (type(None), compat_str))
 | 
					                    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
 | 
					                elif opcode == 164:  # modulo
 | 
				
			||||||
                    value2 = stack.pop()
 | 
					                    value2 = stack.pop()
 | 
				
			||||||
                    value1 = stack.pop()
 | 
					                    value1 = stack.pop()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user