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