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 6'''Unit tests for ChromeScaledImage.''' 7 8 9import re 10import struct 11import unittest 12import zlib 13 14from grit import exception 15from grit import util 16from grit.format import data_pack 17from grit.tool import build 18 19 20_OUTFILETYPES = [ 21 ('.h', 'rc_header'), 22 ('_map.cc', 'resource_map_source'), 23 ('_map.h', 'resource_map_header'), 24 ('.pak', 'data_package'), 25 ('.rc', 'rc_all'), 26] 27 28 29_PNG_HEADER = ( 30 '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52' 31 '\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53' 32 '\xde') 33_PNG_FOOTER = ( 34 '\x00\x00\x00\x0c\x49\x44\x41\x54\x18\x57\x63\xf8\xff\xff\x3f\x00' 35 '\x05\xfe\x02\xfe\xa7\x35\x81\x84\x00\x00\x00\x00\x49\x45\x4e\x44' 36 '\xae\x42\x60\x82') 37 38 39def _MakePNG(chunks): 40 pack_int32 = struct.Struct('>i').pack 41 chunks = [pack_int32(len(payload)) + type + payload + pack_int32(zlib.crc32(type + payload)) 42 for type, payload in chunks] 43 return _PNG_HEADER + ''.join(chunks) + _PNG_FOOTER 44 45 46def _GetFilesInPak(pakname): 47 '''Get a set of the files that were actually included in the .pak output. 48 ''' 49 return set(data_pack.DataPack.ReadDataPack(pakname).resources.values()) 50 51 52def _GetFilesInRc(rcname, tmp_dir, contents): 53 '''Get a set of the files that were actually included in the .rc output. 54 ''' 55 data = util.ReadFile(rcname, util.BINARY).decode('utf-16') 56 contents = dict((tmp_dir.GetPath(k), v) for k, v in contents.items()) 57 return set(contents[m.group(1)] 58 for m in re.finditer(ur'(?m)^\w+\s+BINDATA\s+"([^"]+)"$', data)) 59 60 61def _MakeFallbackAttr(fallback): 62 if fallback is None: 63 return '' 64 else: 65 return ' fallback_to_low_resolution="%s"' % ('false', 'true')[fallback] 66 67 68def _Structures(fallback, *body): 69 return '<structures%s>\n%s\n</structures>' % ( 70 _MakeFallbackAttr(fallback), '\n'.join(body)) 71 72 73def _Structure(name, file, fallback=None): 74 return '<structure name="%s" file="%s" type="chrome_scaled_image"%s />' % ( 75 name, file, _MakeFallbackAttr(fallback)) 76 77 78def _If(expr, *body): 79 return '<if expr="%s">\n%s\n</if>' % (expr, '\n'.join(body)) 80 81 82def _RunBuildTest(self, structures, inputs, expected_outputs, skip_rc=False): 83 outputs = '\n'.join('<output filename="out/%s%s" type="%s" context="%s" />' 84 % (context, ext, type, context) 85 for ext, type in _OUTFILETYPES 86 for context in expected_outputs) 87 infiles = { 88 'in/in.grd': '''<?xml version="1.0" encoding="UTF-8"?> 89 <grit latest_public_release="0" current_release="1"> 90 <outputs> 91 %s 92 </outputs> 93 <release seq="1"> 94 %s 95 </release> 96 </grit> 97 ''' % (outputs, structures), 98 } 99 for pngpath, pngdata in inputs.items(): 100 infiles['in/' + pngpath] = pngdata 101 class Options(object): 102 pass 103 with util.TempDir(infiles) as tmp_dir: 104 with tmp_dir.AsCurrentDir(): 105 options = Options() 106 options.input = tmp_dir.GetPath('in/in.grd') 107 options.verbose = False 108 options.extra_verbose = False 109 build.RcBuilder().Run(options, []) 110 for context, expected_data in expected_outputs.items(): 111 self.assertEquals(expected_data, 112 _GetFilesInPak(tmp_dir.GetPath('out/%s.pak' % context))) 113 if not skip_rc: 114 self.assertEquals(expected_data, 115 _GetFilesInRc(tmp_dir.GetPath('out/%s.rc' % context), 116 tmp_dir, infiles)) 117 118 119class ChromeScaledImageUnittest(unittest.TestCase): 120 def testNormalFallback(self): 121 d123a = _MakePNG([('AbCd', '')]) 122 t123a = _MakePNG([('EfGh', '')]) 123 d123b = _MakePNG([('IjKl', '')]) 124 _RunBuildTest(self, 125 _Structures(None, 126 _Structure('IDR_A', 'a.png'), 127 _Structure('IDR_B', 'b.png'), 128 ), 129 {'default_123_percent/a.png': d123a, 130 'tactile_123_percent/a.png': t123a, 131 'default_123_percent/b.png': d123b, 132 }, 133 {'default_123_percent': set([d123a, d123b]), 134 'tactile_123_percent': set([t123a, d123b]), 135 }) 136 137 def testNormalFallbackFailure(self): 138 self.assertRaises(exception.FileNotFound, 139 _RunBuildTest, self, 140 _Structures(None, 141 _Structure('IDR_A', 'a.png'), 142 ), 143 {'default_100_percent/a.png': _MakePNG([('AbCd', '')]), 144 'tactile_100_percent/a.png': _MakePNG([('EfGh', '')]), 145 }, 146 {'tactile_123_percent': 'should fail before using this'}) 147 148 def testLowresFallback(self): 149 png = _MakePNG([('Abcd', '')]) 150 png_with_csCl = _MakePNG([('csCl', ''),('Abcd', '')]) 151 for outer in (None, False, True): 152 for inner in (None, False, True): 153 args = ( 154 self, 155 _Structures(outer, 156 _Structure('IDR_A', 'a.png', inner), 157 ), 158 {'default_100_percent/a.png': png}, 159 {'tactile_200_percent': set([png_with_csCl])}) 160 if inner or (inner is None and outer): 161 # should fall back to 100% 162 _RunBuildTest(*args, skip_rc=True) 163 else: 164 # shouldn't fall back 165 self.assertRaises(exception.FileNotFound, _RunBuildTest, *args) 166 167 # Test fallback failure with fallback_to_low_resolution=True 168 self.assertRaises(exception.FileNotFound, 169 _RunBuildTest, self, 170 _Structures(True, 171 _Structure('IDR_A', 'a.png'), 172 ), 173 {}, # no files 174 {'tactile_123_percent': 'should fail before using this'}) 175