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.
14 SCRIPT_DIR
= os
.path
.dirname(os
.path
.abspath(__file__
))
15 TOOLS_DIR
= os
.path
.dirname(SCRIPT_DIR
)
16 DATA_DIR
= os
.path
.join(TOOLS_DIR
, 'lib', 'tests', 'data')
17 BUILD_TOOLS_DIR
= os
.path
.join(os
.path
.dirname(TOOLS_DIR
), 'build_tools')
18 CHROME_SRC
= os
.path
.dirname(os
.path
.dirname(os
.path
.dirname(TOOLS_DIR
)))
19 MOCK_DIR
= os
.path
.join(CHROME_SRC
, 'third_party', 'pymock')
21 # For the mock library
22 sys
.path
.append(MOCK_DIR
)
23 sys
.path
.append(TOOLS_DIR
)
24 sys
.path
.append(BUILD_TOOLS_DIR
)
29 from mock
import patch
, Mock
31 TOOLCHAIN_OUT
= os
.path
.join(build_paths
.OUT_DIR
, 'sdk_tests', 'toolchain')
32 NACL_X86_GLIBC_TOOLCHAIN
= os
.path
.join(TOOLCHAIN_OUT
,
33 '%s_x86' % getos
.GetPlatform(),
36 PosixRelPath
= create_nmf
.PosixRelPath
40 """Strip trailing hexidecimal characters from the name of a shared object.
42 It strips everything after the last '.' in the name, and checks that the new
47 libc.so.ad6acbfa => libc.so
48 foo.bar.baz => foo.bar.baz
50 stripped_name
= '.'.join(name
.split('.')[:-1])
51 if stripped_name
.endswith('.so'):
56 class TestPosixRelPath(unittest
.TestCase
):
58 # Note that PosixRelPath only converts from native path format to posix
59 # path format, that's why we have to use os.path.join here.
60 path
= os
.path
.join(os
.path
.sep
, 'foo', 'bar', 'baz.blah')
61 start
= os
.path
.sep
+ 'foo'
62 self
.assertEqual(PosixRelPath(path
, start
), 'bar/baz.blah')
65 class TestDefaultLibpath(unittest
.TestCase
):
67 patcher
= patch('create_nmf.GetSDKRoot', Mock(return_value
='/dummy/path'))
69 self
.addCleanup(patcher
.stop
)
71 def testUsesSDKRoot(self
):
72 paths
= create_nmf
.GetDefaultLibPath('Debug')
74 self
.assertTrue(path
.startswith('/dummy/path'))
76 def testFallbackPath(self
):
77 paths
= create_nmf
.GetDefaultLibPath('foo_Debug')
78 if sys
.platform
== 'win32':
79 paths
= [p
.replace('\\', '/') for p
in paths
]
80 path_base
= '/dummy/path/lib/glibc_x86_64/foo_Debug'
81 path_fallback
= '/dummy/path/lib/glibc_x86_64/Debug'
82 self
.assertIn(path_base
, paths
)
83 self
.assertIn(path_fallback
, paths
)
84 self
.assertGreater(paths
.index(path_fallback
), paths
.index(path_base
))
86 paths
= create_nmf
.GetDefaultLibPath('foo_bar')
87 if sys
.platform
== 'win32':
88 paths
= [p
.replace('\\', '/') for p
in paths
]
89 path_base
= '/dummy/path/lib/glibc_x86_64/foo_bar'
90 path_fallback
= '/dummy/path/lib/glibc_x86_64/Release'
91 self
.assertIn(path_base
, paths
)
92 self
.assertIn(path_fallback
, paths
)
93 self
.assertGreater(paths
.index(path_fallback
), paths
.index(path_base
))
95 def testIncludesNaClPorts(self
):
96 paths
= create_nmf
.GetDefaultLibPath('Debug')
97 self
.assertTrue(any(os
.path
.join('ports', 'lib') in p
for p
in paths
),
98 'naclports libpath missing: %s' % str(paths
))
101 class TestNmfUtils(unittest
.TestCase
):
102 """Tests for the main NmfUtils class in create_nmf."""
106 self
.toolchain
= NACL_X86_GLIBC_TOOLCHAIN
107 self
.objdump
= os
.path
.join(self
.toolchain
, 'bin', 'i686-nacl-objdump')
109 self
.objdump
+= '.exe'
112 def _CreateTestNexe(self
, name
, arch
):
113 """Create an empty test .nexe file for use in create_nmf tests.
115 This is used rather than checking in test binaries since the
116 checked in binaries depend on .so files that only exist in the
117 certain SDK that build them.
119 compiler
= os
.path
.join(self
.toolchain
, 'bin', '%s-nacl-g++' % arch
)
122 os
.environ
['CYGWIN'] = 'nodosfilewarning'
123 program
= 'int main() { return 0; }'
124 name
= os
.path
.join(self
.tempdir
, name
)
125 dst_dir
= os
.path
.dirname(name
)
126 if not os
.path
.exists(dst_dir
):
128 self
.assertTrue(os
.path
.exists(compiler
), 'compiler missing: %s' % compiler
)
129 cmd
= [compiler
, '-pthread', '-x' , 'c', '-o', name
, '-']
130 p
= subprocess
.Popen(cmd
, stdin
=subprocess
.PIPE
)
131 p
.communicate(input=program
)
132 self
.assertEqual(p
.returncode
, 0)
137 shutil
.rmtree(self
.tempdir
)
140 self
.tempdir
= tempfile
.mkdtemp()
142 def _CreateNmfUtils(self
, nexes
, **kwargs
):
143 if not kwargs
.get('lib_path'):
144 # Use lib instead of lib64 (lib64 is a symlink to lib).
145 kwargs
['lib_path'] = [
146 os
.path
.join(self
.toolchain
, 'x86_64-nacl', 'lib'),
147 os
.path
.join(self
.toolchain
, 'x86_64-nacl', 'lib32')]
148 return create_nmf
.NmfUtils(nexes
,
149 objdump
=self
.objdump
,
152 def _CreateStatic(self
, arch_path
=None, **kwargs
):
153 """Copy all static .nexe files from the DATA_DIR to a temporary directory.
156 arch_path: A dictionary mapping architecture to the directory to generate
157 the .nexe for the architecture in.
158 kwargs: Keyword arguments to pass through to create_nmf.NmfUtils
162 A tuple with 2 elements:
163 * The generated NMF as a dictionary (i.e. parsed by json.loads)
164 * A list of the generated .nexe paths
166 arch_path
= arch_path
or {}
168 for arch
in ('x86_64', 'x86_32', 'arm'):
169 nexe_name
= 'test_static_%s.nexe' % arch
170 src_nexe
= os
.path
.join(DATA_DIR
, nexe_name
)
171 dst_nexe
= os
.path
.join(self
.tempdir
, arch_path
.get(arch
, ''), nexe_name
)
172 dst_dir
= os
.path
.dirname(dst_nexe
)
173 if not os
.path
.exists(dst_dir
):
175 shutil
.copy(src_nexe
, dst_nexe
)
176 nexes
.append(dst_nexe
)
179 nmf_utils
= self
._CreateNmfUtils
(nexes
, **kwargs
)
180 nmf
= json
.loads(nmf_utils
.GetJson())
183 def _CreateDynamicAndStageDeps(self
, arch_path
=None, **kwargs
):
184 """Create dynamic .nexe files and put them in a temporary directory, with
185 their dependencies staged in the same directory.
188 arch_path: A dictionary mapping architecture to the directory to generate
189 the .nexe for the architecture in.
190 kwargs: Keyword arguments to pass through to create_nmf.NmfUtils
194 A tuple with 2 elements:
195 * The generated NMF as a dictionary (i.e. parsed by json.loads)
196 * A list of the generated .nexe paths
198 arch_path
= arch_path
or {}
200 for arch
in ('x86_64', 'x86_32'):
201 nexe_name
= 'test_dynamic_%s.nexe' % arch
202 rel_nexe
= os
.path
.join(arch_path
.get(arch
, ''), nexe_name
)
203 arch_alt
= 'i686' if arch
== 'x86_32' else arch
204 nexe
= self
._CreateTestNexe
(rel_nexe
, arch_alt
)
208 nmf_utils
= self
._CreateNmfUtils
(nexes
, **kwargs
)
209 nmf
= json
.loads(nmf_utils
.GetJson())
210 nmf_utils
.StageDependencies(self
.tempdir
)
214 def _CreatePexe(self
, **kwargs
):
215 """Copy test.pexe from the DATA_DIR to a temporary directory.
218 kwargs: Keyword arguments to pass through to create_nmf.NmfUtils
222 A tuple with 2 elements:
223 * The generated NMF as a dictionary (i.e. parsed by json.loads)
224 * A list of the generated .pexe paths
226 pexe_name
= 'test.pexe'
227 src_pexe
= os
.path
.join(DATA_DIR
, pexe_name
)
228 dst_pexe
= os
.path
.join(self
.tempdir
, pexe_name
)
229 shutil
.copy(src_pexe
, dst_pexe
)
232 nmf_utils
= self
._CreateNmfUtils
(pexes
, **kwargs
)
233 nmf
= json
.loads(nmf_utils
.GetJson())
237 def _CreateBitCode(self
, **kwargs
):
238 """Copy test.bc from the DATA_DIR to a temporary directory.
241 kwargs: Keyword arguments to pass through to create_nmf.NmfUtils
245 A tuple with 2 elements:
246 * The generated NMF as a dictionary (i.e. parsed by json.loads)
247 * A list of the generated .bc paths
250 src_bc
= os
.path
.join(DATA_DIR
, bc_name
)
251 dst_bc
= os
.path
.join(self
.tempdir
, bc_name
)
252 shutil
.copy(src_bc
, dst_bc
)
255 nmf_utils
= self
._CreateNmfUtils
(bcs
, **kwargs
)
256 nmf
= json
.loads(nmf_utils
.GetJson())
260 def assertManifestEquals(self
, manifest
, expected
):
261 """Compare two manifest dictionaries.
263 The input manifest is regenerated with all string keys and values being
264 processed through StripSo, to remove the random hexidecimal characters at
265 the end of shared object names.
268 manifest: The generated manifest.
269 expected: The expected manifest.
271 def StripSoCopyDict(d
):
273 for k
, v
in d
.iteritems():
275 if isinstance(v
, (str, unicode)):
277 elif type(v
) is list:
279 elif type(v
) is dict:
280 new_v
= StripSoCopyDict(v
)
282 # Assume that anything else can be copied directly.
288 self
.assertEqual(StripSoCopyDict(manifest
), expected
)
290 def assertStagingEquals(self
, expected
):
291 """Compare the contents of the temporary directory, to an expected
295 expected: The expected directory layout.
298 for root
, _
, files
in os
.walk(self
.tempdir
):
299 rel_root_posix
= PosixRelPath(root
, self
.tempdir
)
301 path
= posixpath
.join(rel_root_posix
, StripSo(f
))
302 if path
.startswith('./'):
304 all_files
.append(path
)
305 self
.assertEqual(set(expected
), set(all_files
))
308 def testStatic(self
):
309 nmf
, _
= self
._CreateStatic
()
310 expected_manifest
= {
313 'x86-64': {'url': 'test_static_x86_64.nexe'},
314 'x86-32': {'url': 'test_static_x86_32.nexe'},
315 'arm': {'url': 'test_static_arm.nexe'},
318 self
.assertManifestEquals(nmf
, expected_manifest
)
320 def testStaticWithPath(self
):
321 arch_dir
= {'x86_32': 'x86_32', 'x86_64': 'x86_64', 'arm': 'arm'}
322 nmf
, _
= self
._CreateStatic
(arch_dir
, nmf_root
=self
.tempdir
)
323 expected_manifest
= {
326 'x86-32': {'url': 'x86_32/test_static_x86_32.nexe'},
327 'x86-64': {'url': 'x86_64/test_static_x86_64.nexe'},
328 'arm': {'url': 'arm/test_static_arm.nexe'},
331 self
.assertManifestEquals(nmf
, expected_manifest
)
333 def testStaticWithPathNoNmfRoot(self
):
334 # This case is not particularly useful, but it is similar to how create_nmf
335 # used to work. If there is no nmf_root given, all paths are relative to
336 # the first nexe passed on the commandline. I believe the assumption
337 # previously was that all .nexes would be in the same directory.
338 arch_dir
= {'x86_32': 'x86_32', 'x86_64': 'x86_64', 'arm': 'arm'}
339 nmf
, _
= self
._CreateStatic
(arch_dir
)
340 expected_manifest
= {
343 'x86-32': {'url': '../x86_32/test_static_x86_32.nexe'},
344 'x86-64': {'url': '../x86_64/test_static_x86_64.nexe'},
345 'arm': {'url': 'test_static_arm.nexe'},
348 self
.assertManifestEquals(nmf
, expected_manifest
)
350 def testStaticWithNexePrefix(self
):
351 nmf
, _
= self
._CreateStatic
(nexe_prefix
='foo')
352 expected_manifest
= {
355 'x86-64': {'url': 'foo/test_static_x86_64.nexe'},
356 'x86-32': {'url': 'foo/test_static_x86_32.nexe'},
357 'arm': {'url': 'foo/test_static_arm.nexe'},
360 self
.assertManifestEquals(nmf
, expected_manifest
)
362 def testDynamic(self
):
363 nmf
, nexes
= self
._CreateDynamicAndStageDeps
()
364 expected_manifest
= {
367 'x86-32': {'url': 'test_dynamic_x86_32.nexe'},
368 'x86-64': {'url': 'test_dynamic_x86_64.nexe'},
371 'x86-32': {'url': 'lib32/libc.so'},
372 'x86-64': {'url': 'lib64/libc.so'},
375 'x86-32': {'url': 'lib32/libgcc_s.so'},
376 'x86-64': {'url': 'lib64/libgcc_s.so'},
379 'x86-32': {'url': 'lib32/libpthread.so'},
380 'x86-64': {'url': 'lib64/libpthread.so'},
384 'x86-32': {'url': 'lib32/runnable-ld.so'},
385 'x86-64': {'url': 'lib64/runnable-ld.so'},
389 expected_staging
= [os
.path
.basename(f
) for f
in nexes
]
390 expected_staging
.extend([
393 'lib32/libpthread.so',
394 'lib32/runnable-ld.so',
397 'lib64/libpthread.so',
398 'lib64/runnable-ld.so'])
400 self
.assertManifestEquals(nmf
, expected_manifest
)
401 self
.assertStagingEquals(expected_staging
)
403 def testDynamicWithPath(self
):
404 arch_dir
= {'x86_64': 'x86_64', 'x86_32': 'x86_32'}
405 nmf
, nexes
= self
._CreateDynamicAndStageDeps
(arch_dir
,
406 nmf_root
=self
.tempdir
)
407 expected_manifest
= {
410 'x86-32': {'url': 'x86_32/test_dynamic_x86_32.nexe'},
411 'x86-64': {'url': 'x86_64/test_dynamic_x86_64.nexe'},
414 'x86-32': {'url': 'x86_32/lib32/libc.so'},
415 'x86-64': {'url': 'x86_64/lib64/libc.so'},
418 'x86-32': {'url': 'x86_32/lib32/libgcc_s.so'},
419 'x86-64': {'url': 'x86_64/lib64/libgcc_s.so'},
422 'x86-32': {'url': 'x86_32/lib32/libpthread.so'},
423 'x86-64': {'url': 'x86_64/lib64/libpthread.so'},
427 'x86-32': {'url': 'x86_32/lib32/runnable-ld.so'},
428 'x86-64': {'url': 'x86_64/lib64/runnable-ld.so'},
432 expected_staging
= [PosixRelPath(f
, self
.tempdir
) for f
in nexes
]
433 expected_staging
.extend([
434 'x86_32/lib32/libc.so',
435 'x86_32/lib32/libgcc_s.so',
436 'x86_32/lib32/libpthread.so',
437 'x86_32/lib32/runnable-ld.so',
438 'x86_64/lib64/libc.so',
439 'x86_64/lib64/libgcc_s.so',
440 'x86_64/lib64/libpthread.so',
441 'x86_64/lib64/runnable-ld.so'])
443 self
.assertManifestEquals(nmf
, expected_manifest
)
444 self
.assertStagingEquals(expected_staging
)
446 def testDynamicWithRelPath(self
):
447 """Test that when the nmf root is a relative path that things work."""
448 arch_dir
= {'x86_64': 'x86_64', 'x86_32': 'x86_32'}
449 old_path
= os
.getcwd()
451 os
.chdir(self
.tempdir
)
452 nmf
, nexes
= self
._CreateDynamicAndStageDeps
(arch_dir
, nmf_root
='')
453 expected_manifest
= {
456 'x86-32': {'url': 'x86_32/test_dynamic_x86_32.nexe'},
457 'x86-64': {'url': 'x86_64/test_dynamic_x86_64.nexe'},
460 'x86-32': {'url': 'x86_32/lib32/libc.so'},
461 'x86-64': {'url': 'x86_64/lib64/libc.so'},
464 'x86-32': {'url': 'x86_32/lib32/libgcc_s.so'},
465 'x86-64': {'url': 'x86_64/lib64/libgcc_s.so'},
468 'x86-32': {'url': 'x86_32/lib32/libpthread.so'},
469 'x86-64': {'url': 'x86_64/lib64/libpthread.so'},
473 'x86-32': {'url': 'x86_32/lib32/runnable-ld.so'},
474 'x86-64': {'url': 'x86_64/lib64/runnable-ld.so'},
478 expected_staging
= [PosixRelPath(f
, self
.tempdir
) for f
in nexes
]
479 expected_staging
.extend([
480 'x86_32/lib32/libc.so',
481 'x86_32/lib32/libgcc_s.so',
482 'x86_32/lib32/libpthread.so',
483 'x86_32/lib32/runnable-ld.so',
484 'x86_64/lib64/libc.so',
485 'x86_64/lib64/libgcc_s.so',
486 'x86_64/lib64/libpthread.so',
487 'x86_64/lib64/runnable-ld.so'])
489 self
.assertManifestEquals(nmf
, expected_manifest
)
490 self
.assertStagingEquals(expected_staging
)
494 def testDynamicWithPathNoArchPrefix(self
):
495 arch_dir
= {'x86_64': 'x86_64', 'x86_32': 'x86_32'}
496 nmf
, nexes
= self
._CreateDynamicAndStageDeps
(arch_dir
,
497 nmf_root
=self
.tempdir
,
499 expected_manifest
= {
502 'x86-32': {'url': 'x86_32/test_dynamic_x86_32.nexe'},
503 'x86-64': {'url': 'x86_64/test_dynamic_x86_64.nexe'},
506 'x86-32': {'url': 'x86_32/libc.so'},
507 'x86-64': {'url': 'x86_64/libc.so'},
510 'x86-32': {'url': 'x86_32/libgcc_s.so'},
511 'x86-64': {'url': 'x86_64/libgcc_s.so'},
514 'x86-32': {'url': 'x86_32/libpthread.so'},
515 'x86-64': {'url': 'x86_64/libpthread.so'},
519 'x86-32': {'url': 'x86_32/runnable-ld.so'},
520 'x86-64': {'url': 'x86_64/runnable-ld.so'},
524 expected_staging
= [PosixRelPath(f
, self
.tempdir
) for f
in nexes
]
525 expected_staging
.extend([
527 'x86_32/libgcc_s.so',
528 'x86_32/libpthread.so',
529 'x86_32/runnable-ld.so',
531 'x86_64/libgcc_s.so',
532 'x86_64/libpthread.so',
533 'x86_64/runnable-ld.so'])
535 self
.assertManifestEquals(nmf
, expected_manifest
)
536 self
.assertStagingEquals(expected_staging
)
538 def testDynamicWithLibPrefix(self
):
539 nmf
, nexes
= self
._CreateDynamicAndStageDeps
(lib_prefix
='foo')
540 expected_manifest
= {
543 'x86-32': {'url': 'test_dynamic_x86_32.nexe'},
544 'x86-64': {'url': 'test_dynamic_x86_64.nexe'},
547 'x86-32': {'url': 'foo/lib32/libc.so'},
548 'x86-64': {'url': 'foo/lib64/libc.so'},
551 'x86-32': {'url': 'foo/lib32/libgcc_s.so'},
552 'x86-64': {'url': 'foo/lib64/libgcc_s.so'},
555 'x86-32': {'url': 'foo/lib32/libpthread.so'},
556 'x86-64': {'url': 'foo/lib64/libpthread.so'},
560 'x86-32': {'url': 'foo/lib32/runnable-ld.so'},
561 'x86-64': {'url': 'foo/lib64/runnable-ld.so'},
565 expected_staging
= [PosixRelPath(f
, self
.tempdir
) for f
in nexes
]
566 expected_staging
.extend([
568 'foo/lib32/libgcc_s.so',
569 'foo/lib32/libpthread.so',
570 'foo/lib32/runnable-ld.so',
572 'foo/lib64/libgcc_s.so',
573 'foo/lib64/libpthread.so',
574 'foo/lib64/runnable-ld.so'])
576 self
.assertManifestEquals(nmf
, expected_manifest
)
577 self
.assertStagingEquals(expected_staging
)
580 nmf
, _
= self
._CreatePexe
()
581 expected_manifest
= {
590 self
.assertManifestEquals(nmf
, expected_manifest
)
592 def testPexeOptLevel(self
):
593 nmf
, _
= self
._CreatePexe
(pnacl_optlevel
=2)
594 expected_manifest
= {
604 self
.assertManifestEquals(nmf
, expected_manifest
)
606 def testBitCode(self
):
607 nmf
, _
= self
._CreateBitCode
(pnacl_debug_optlevel
=0)
608 expected_manifest
= {
618 self
.assertManifestEquals(nmf
, expected_manifest
)
621 if __name__
== '__main__':