• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7#
8# Unless required by applicable law or agreed to in writing, software
9# distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
13
14require_relative 'runner'
15require_relative 'result'
16require_relative 'reporters/test_reporter'
17
18module TestRunner
19  class SingleTestRunner
20    def initialize(file, id, reporter_class, root_dir, report_dir)
21      @pa_file = file
22      @bin_file = "#{$tmp_dir}#{File.basename(file)}.bin"
23      @id = id
24      @repro_cmds = []
25      @test_failed = false
26      @reporter = reporter_class.new root_dir, @pa_file, report_dir
27      @result = TestRunner::Result.new @reporter
28      lines = File.foreach(@pa_file)
29                  .grep(/^\s*##\s*runner-option\s*:\s*[\S*\s*]*\s*$/)
30                  .flat_map { |s| s.split(':', 2).map(&:strip) }
31                  .reject { |s| s.match(/^#/) }
32                  .uniq
33      @runner_options = lines.each_with_object({}) do |k, h|
34        if k.include? 'tags:'
35          tags = TestRunner.split_separated_by_colon k
36          h['tags'] = tags
37        elsif k.include? 'bugid:'
38          bugs = TestRunner.split_separated_by_colon k
39          h['bug_ids'] = bugs
40          @reporter.log_bugids(bugs)
41        else
42          h[k] = true
43        end
44      end
45      @pa_options = if @runner_options.include? 'use-pa'
46                        '--load-runtimes="core" ' \
47                        '--boot-panda-files=' \
48                        "#{$path_to_panda}/pandastdlib/arkstdlib.abc"
49                      else
50                        ''
51                      end
52
53      @verifier_options = $verbose_verifier ? '--log-components=verifier --log-level=debug ' : ''
54
55      @verifier_config_args = if (@runner_options.key?('verifier-config') || verifier_forced?) && !$verifier_config.empty?
56                                "--config-file=#{$verifier_config} "
57                              else
58                                ''
59                              end
60
61      if @runner_options.include? 'main-exitcode-wrapper'
62        @main_function = '_GLOBAL::main_exitcode_wrapper'
63        # Exit code value for wrapped main
64        # this value is used to determine false-positive cases
65        @expected_passed_exit_code = 80
66        @expected_failed_exit_code = 81
67      else
68        @main_function = '_GLOBAL::main'
69        # Default exit code value for main function
70        @expected_passed_exit_code = 0
71        @expected_failed_exit_code = 1
72      end
73
74      @test_panda_options = ''
75      previous_line_empty = false # exit loop on two consecutive empty lines
76      File.foreach(@pa_file) do |line|
77        if match = line.match(/^## panda-options: (.+)\s*$/)
78          @test_panda_options = match.captures[0]
79          break # the line we are looking for is found
80        elsif line.strip.empty?
81          break if previous_line_empty
82
83          previous_line_empty = true
84        else
85          previous_line_empty = false
86        end
87      end
88    end
89
90    def error?(status)
91      [
92        ERROR_NODATA,
93        ERROR_CANNOT_CREATE_PROCESS,
94        ERROR_TIMEOUT
95      ].include? status
96    end
97
98    def in_exclude_list?
99      !(@runner_options['tags'] & $exclude_list || []).empty?
100    end
101
102    def in_include_list?
103      !(@runner_options['tags'] & $include_list || []).empty?
104    end
105
106    def in_bugids_list?
107      !(@runner_options['bug_ids'] & $bug_ids || []).empty?
108    end
109
110    def compile_only?
111      (@runner_options.include? 'compile-failure' or
112        @runner_options.include? 'compile-only')
113    end
114
115    def verifier_only?
116      (@runner_options.include? 'verifier-failure' or
117        @runner_options.include? 'verifier-only')
118    end
119
120    def verifier_forced?
121      ($force_verifier and
122        !(@runner_options.include? 'verifier-failure' or
123          @runner_options.include? 'verifier-only' or
124          @runner_options.include? 'compile-failure'))
125    end
126
127    def test_failed?
128      @test_failed
129    end
130
131    def test_failed
132      @test_failed = true
133    end
134
135    def cleanup
136      @reporter.verbose_log "# Cleanup - remove #{@bin_file}, if exists"
137      FileUtils.rm(@bin_file) if File.exist? @bin_file
138      FileUtils.rm("#{@bin_file}.aot") if $paoc && File.exist?("#{@bin_file}.aot")
139    end
140
141    def compilation
142      output, status, _core = run_pandasm
143      case status
144      when 0
145        if @runner_options['compile-failure']
146          @result.update_negative_passed_compilation output, @pa_file
147          test_failed
148        elsif @runner_options['compile-only']
149          @result.update_compilation_passed output, @pa_file
150        end
151      when 1
152        if @runner_options['compile-failure']
153          @result.update_failed_negative_compilation output, @pa_file
154        else
155          @result.update_failed_compilation output, @pa_file
156          test_failed
157        end
158      else
159        @result.update_failed_compilation output, @pa_file
160        test_failed
161      end
162    end
163
164    def run_pandasm
165      run_command "#{$pandasm} #{@pa_file} #{@bin_file}"
166    end
167
168    def verification
169      output, status, core = run_verifier
170      case status
171      when 0
172        if @runner_options['verifier-failure']
173          @result.update_verifier_negative_failure output, status, @pa_file
174          test_failed
175        else
176          @result.update_passed output, status, @pa_file
177        end
178      when 255
179        if @runner_options['verifier-failure']
180          @result.update_passed output, status, @pa_file
181        else
182          @result.update_verifier_failure output, status, @pa_file, core
183          test_failed
184        end
185      else
186        @result.update_verifier_failure output, status, @pa_file, core
187        test_failed
188      end
189    end
190
191    def run_verifier
192      run_command "#{$verifier} #{@verifier_options} #{@verifier_config_args} #{@pa_options} #{@bin_file}"
193    end
194
195    def execution
196      output, status, core = run_panda
197      case status
198      when @expected_passed_exit_code
199        if @runner_options['run-failure']
200          @result.update_run_negative_failure output, status, @pa_file
201          test_failed
202        else
203          @result.update_passed output, status, @pa_file
204        end
205      when @expected_failed_exit_code, 1 # '1' for the case when Exception is not caught in test
206        if @runner_options['run-failure']
207          @result.update_passed output, status, @pa_file
208        else
209          @result.update_run_failure output, status, @pa_file, core
210          test_failed
211        end
212      else
213        # Test ended abnormally
214        @result.update_run_failure output, status, @pa_file, core
215        test_failed
216      end
217    end
218
219    def run_panda
220      aot = if $paoc
221              "--aot-file=#{@bin_file}.aot"
222            else
223              ''
224            end
225      run_command "#{$panda} #{@pa_options} #{$panda_options.join ' '} #{@test_panda_options} #{aot} " \
226            "#{@bin_file} #{@main_function}"
227    end
228
229    def aot_compilation
230      output, status, _core = run_paoc
231      return if status.zero?
232
233      @result.update_failed_compilation output, @pa_file
234      test_failed
235    end
236
237    def run_paoc
238      run_command "#{$paoc} #{@pa_options} --paoc-panda-files #{@bin_file} --paoc-output #{@bin_file}.aot"
239    end
240
241    def quickening
242      output, status, _core = run_quickener
243      return if status.zero?
244
245      @result.update_failed_quickening output, @pa_file
246      test_failed
247    end
248
249    def run_quickener
250      run_command "#{$quickener} #{@bin_file} #{@bin_file}"
251    end
252
253    def run_command(cmd)
254      @repro_cmds << "#{cmd} ; echo __$?__"
255      TestRunner::CommandRunner.new(cmd, @reporter).run_cmd
256    end
257
258    def process_single
259      @reporter.prologue
260      process_single_inner
261      @reporter.epilogue
262    end
263
264    def process_single_inner
265      @reporter.verbose_log '# List of runner options'
266      @reporter.verbose_log "verifier-failure      = #{@runner_options['verifier-failure']}"
267      @reporter.verbose_log "verifier-only         = #{@runner_options['verifier-only']}"
268      @reporter.verbose_log "compiler-failure      = #{@runner_options['compile-failure']}"
269      @reporter.verbose_log "compiler-only         = #{@runner_options['compile-only']}"
270      @reporter.verbose_log "failure               = #{@runner_options['run-failure']}"
271      @reporter.verbose_log "use-pa     = #{@runner_options['use-pa']}"
272      @reporter.verbose_log "bugid                 = #{@runner_options['bug_ids']}"
273      @reporter.verbose_log "tags                  = #{@runner_options['tags']}"
274      @reporter.verbose_log "ignore                = #{@runner_options['ignore']}"
275      @reporter.verbose_log "verifier-config       = #{@runner_options['verifier-config']}"
276      @reporter.verbose_log "main-exitcode-wrapper = #{@runner_options['main-exitcode-wrapper']}"
277
278      # 1) Check step
279      if in_exclude_list?
280        @reporter.log_exclusion
281        @result.update_excluded @pa_file
282        return
283      end
284
285      if $include_list != [] && !in_include_list?
286        @reporter.log_skip_include
287        return
288      end
289
290      # Check for bugid
291      if $bug_ids != [] && !in_bugids_list?
292        @reporter.log_skip_bugid
293        return
294      end
295
296      run_all_and_ignored = $run_all | $run_ignore
297
298      if @runner_options['ignore'] & !run_all_and_ignored
299        @reporter.log_skip_ignore
300        return
301      end
302
303      if @runner_options['use-pa'] & !run_all_and_ignored
304        @reporter.log_skip_ignore
305        return
306      end
307
308      if !@runner_options['ignore'] & $run_ignore & !$run_all
309        @reporter.log_skip_only_ignore
310        return
311      end
312
313      @reporter.log_ignore_ignored if @runner_options['ignore'] & ($run_all | $run_ignore)
314      @reporter.verbose_log ''
315      cleanup
316
317      # 2) Compilation
318      compilation
319      return if test_failed? || compile_only?
320
321      # 3) Quickening
322      if $quckener
323        quickening
324        return if test_failed?
325      end
326
327      # 4) Verification
328      if verifier_only? || verifier_forced?
329        verification
330        return
331      end
332
333      # 5) AOT Compilation
334      if $paoc
335        if @runner_options['run-failure']
336          @reporter.log_exclusion
337          @result.update_excluded @pa_file
338          return
339        else
340          aot_compilation
341          return if test_failed?
342        end
343      end
344
345      # 6) Execution
346      execution
347    ensure
348      if test_failed?
349        @reporter.log_repro_commands @repro_cmds
350      else
351        cleanup
352      end
353    end
354  end
355end
356