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