1 2# Copyright (c) 2011 The Native Client 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 6import os 7import sys 8sys.path.append(os.path.join(os.path.dirname(__file__), 9 "..", "..", "..", "tests")) 10 11import re 12import subprocess 13import unittest 14 15from testutils import read_file, write_file 16import testutils 17 18 19ANNOTATE_DIR = os.path.join(os.path.dirname(__file__), "driver") 20 21NACL_CFLAGS = os.environ.get("NACL_CFLAGS", "").split() 22 23 24class ToolchainTests(testutils.TempDirTestCase): 25 26 def test_ncval_returns_errors(self): 27 # Check that ncval returns a non-zero return code when there is a 28 # validation failure. 29 source = """ 30void _start() { 31#if defined(__i386__) || defined(__x86_64__) 32 __asm__("ret"); /* This comment appears in output */ 33#else 34# error Update this test for other architectures! 35#endif 36 return 0; 37} 38""" 39 temp_dir = self.make_temp_dir() 40 source_file = os.path.join(temp_dir, "code.c") 41 write_file(source_file, source) 42 testutils.check_call(["x86_64-nacl-gcc", "-g", "-nostartfiles", "-nostdlib", 43 source_file, 44 "-o", os.path.join(temp_dir, "prog")] + NACL_CFLAGS) 45 dest_file = os.path.join(self.make_temp_dir(), "file") 46 rc = subprocess.call([sys.executable, 47 os.path.join(ANNOTATE_DIR, "ncval_annotate.py"), 48 os.path.join(temp_dir, "prog")], 49 stdout=open(dest_file, "w")) 50 # ncval_annotate should propagate the exit code through so that it 51 # can be used as a substitute for ncval. 52 self.assertEquals(rc, 1) 53 54 # Errors printed in two lines, with interspersed objdump output. 55 # The first line starts with an ADDRESS and file/line. 56 # The second is the error on the instruction, which ends with "<<<<". 57 filter_pattern = "^[0-9a-f]+.*|.*<<<<$" 58 actual_lines = [line for line in open(dest_file, "r") 59 if re.match(filter_pattern, line)] 60 actual = "".join(actual_lines) 61 # Strip windows' carriage return characters. 62 actual = actual.replace("\r", "") 63 64 expected_pattern = """ 65ADDRESS \(FILENAME:[0-9]+, function _start\): unrecognized instruction 66\s+ADDRESS:\s+c3\s+retq?\s+<<<< 67""" 68 expected_pattern = expected_pattern.replace("ADDRESS", "[0-9a-f]+") 69 # Cygwin mixes \ and / in filenames, so be liberal in what we accept. 70 expected_pattern = expected_pattern.replace("FILENAME", "code.c") 71 72 if re.match(expected_pattern, "\n" + actual) is None: 73 raise AssertionError( 74 "Output did not match.\n\nEXPECTED:\n%s\nACTUAL:\n%s" 75 % (expected_pattern, actual)) 76 77 def test_ncval_handles_many_errors(self): 78 # This tests for 79 # http://code.google.com/p/nativeclient/issues/detail?id=915, 80 # where ncval_annotate would truncate the number of results at 100. 81 disallowed = """ 82#if defined(__i386__) || defined(__x86_64__) 83 __asm__("int $0x80"); 84#else 85# error Update this test for other architectures! 86#endif 87""" 88 source = "void _start() { %s }" % (disallowed * 150) 89 temp_dir = self.make_temp_dir() 90 source_file = os.path.join(temp_dir, "code.c") 91 write_file(source_file, source) 92 testutils.check_call(["x86_64-nacl-gcc", "-g", "-nostartfiles", "-nostdlib", 93 source_file, 94 "-o", os.path.join(temp_dir, "prog")] + NACL_CFLAGS) 95 dest_file = os.path.join(self.make_temp_dir(), "file") 96 subprocess.call([sys.executable, 97 os.path.join(ANNOTATE_DIR, "ncval_annotate.py"), 98 os.path.join(temp_dir, "prog")], 99 stdout=open(dest_file, "w")) 100 # Filter unrecognized instructions that are printed, one per bundle. 101 filter_pattern = ".*<<<<$" 102 failures = len([line for line in open(dest_file, "r") 103 if re.match(filter_pattern, line)]) 104 self.assertEquals(failures, 10) 105 106 107if __name__ == "__main__": 108 unittest.main() 109