1#!/usr/bin/env python3
2#
3# Copyright (C) 2014 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#   http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import argparse
18import os
19
20from common.archs import archs_list
21from common.logger import Logger
22from file_format.c1visualizer.parser import parse_c1_visualizer_stream
23from file_format.checker.parser import parse_checker_stream
24from match.file import match_files
25
26
27def parse_arguments():
28  parser = argparse.ArgumentParser()
29  parser.add_argument("tested_file",
30                      help="text file the checks should be verified against")
31  parser.add_argument("source_path", nargs="?",
32                      help="path to file/folder with checking annotations")
33  parser.add_argument("--check-prefix", dest="check_prefix", default="CHECK", metavar="PREFIX",
34                      help="prefix of checks in the test files (default: CHECK)")
35  parser.add_argument("--list-passes", dest="list_passes", action="store_true",
36                      help="print a list of all passes found in the tested file")
37  parser.add_argument("--dump-pass", dest="dump_pass", metavar="PASS",
38                      help="print a compiler pass dump")
39  parser.add_argument("--arch", dest="arch", choices=archs_list,
40                      help="Run tests for the specified target architecture.")
41  parser.add_argument("--debuggable", action="store_true",
42                      help="Run tests for debuggable code.")
43  parser.add_argument("--print-cfg", action="store_true", default="True", dest="print_cfg",
44                      help="Print the whole cfg file in case of test failure (default)")
45  parser.add_argument("--no-print-cfg", action="store_false", default="True", dest="print_cfg",
46                      help="Don't print the whole cfg file in case of test failure")
47  parser.add_argument("-q", "--quiet", action="store_true",
48                      help="print only errors")
49  return parser.parse_args()
50
51
52def list_passes(output_filename):
53  c1_file = parse_c1_visualizer_stream(output_filename, open(output_filename, "r"))
54  for compiler_pass in c1_file.passes:
55    Logger.log(compiler_pass.name)
56
57
58def dump_pass(output_filename, pass_name):
59  c1_file = parse_c1_visualizer_stream(output_filename, open(output_filename, "r"))
60  compiler_pass = c1_file.find_pass(pass_name)
61  if compiler_pass:
62    max_line_no = compiler_pass.start_line_no + len(compiler_pass.body)
63    len_line_no = len(str(max_line_no)) + 2
64    cur_line_no = compiler_pass.start_line_no
65    for line in compiler_pass.body:
66      Logger.log((str(cur_line_no) + ":").ljust(len_line_no) + line)
67      cur_line_no += 1
68  else:
69    Logger.fail('Pass "{}" not found in the output'.format(pass_name))
70
71
72def find_checker_files(path):
73  """ Returns a list of files to scan for check annotations in the given path.
74      Path to a file is returned as a single-element list, directories are
75      recursively traversed and all '.java', '.j', and '.smali' files returned.
76  """
77  if not path:
78    Logger.fail("No source path provided")
79  elif os.path.isfile(path):
80    return [path]
81  elif os.path.isdir(path):
82    found_files = []
83    for root, dirs, files in os.walk(path):
84      for file in files:
85        extension = os.path.splitext(file)[1]
86        if extension in [".java", ".smali", ".j"]:
87          found_files.append(os.path.join(root, file))
88    return found_files
89  else:
90    Logger.fail('Source path "{}" not found'.format(path))
91
92
93def run_tests(check_prefix, check_path, output_filename, target_arch, debuggable_mode, print_cfg):
94  c1_file = parse_c1_visualizer_stream(output_filename, open(output_filename, "r"))
95  for check_filename in find_checker_files(check_path):
96    checker_file = parse_checker_stream(os.path.basename(check_filename),
97                                        check_prefix,
98                                        open(check_filename, "r"),
99                                        target_arch)
100    match_files(checker_file, c1_file, target_arch, debuggable_mode, print_cfg)
101
102
103if __name__ == "__main__":
104  args = parse_arguments()
105
106  if args.quiet:
107    Logger.Verbosity = Logger.Level.ERROR
108
109  if args.list_passes:
110    list_passes(args.tested_file)
111  elif args.dump_pass:
112    dump_pass(args.tested_file, args.dump_pass)
113  else:
114    run_tests(args.check_prefix, args.source_path, args.tested_file, args.arch, args.debuggable,
115              args.print_cfg)
116