1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
|
# Copyright (c) 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A number of common spirv result checks coded in mixin classes.
A test case can use these checks by declaring their enclosing mixin classes
as superclass and providing the expected_* variables required by the check_*()
methods in the mixin classes.
"""
import difflib
import functools
import os
import re
import subprocess
import traceback
from spirv_test_framework import SpirvTest
from builtins import bytes
DEFAULT_SPIRV_VERSION = 0x010000
def convert_to_unix_line_endings(source):
"""Converts all line endings in source to be unix line endings."""
result = source.replace('\r\n', '\n').replace('\r', '\n')
return result
def substitute_file_extension(filename, extension):
"""Substitutes file extension, respecting known shader extensions.
foo.vert -> foo.vert.[extension] [similarly for .frag, .comp, etc.]
foo.glsl -> foo.[extension]
foo.unknown -> foo.[extension]
foo -> foo.[extension]
"""
if filename[-5:] not in [
'.vert', '.frag', '.tesc', '.tese', '.geom', '.comp', '.spvasm'
]:
return filename.rsplit('.', 1)[0] + '.' + extension
else:
return filename + '.' + extension
def get_object_filename(source_filename):
"""Gets the object filename for the given source file."""
return substitute_file_extension(source_filename, 'spv')
def get_assembly_filename(source_filename):
"""Gets the assembly filename for the given source file."""
return substitute_file_extension(source_filename, 'spvasm')
def verify_file_non_empty(filename):
"""Checks that a given file exists and is not empty."""
if not os.path.isfile(filename):
return False, 'Cannot find file: ' + filename
if not os.path.getsize(filename):
return False, 'Empty file: ' + filename
return True, ''
class ReturnCodeIsZero(SpirvTest):
"""Mixin class for checking that the return code is zero."""
def check_return_code_is_zero(self, status):
if status.returncode:
return False, 'Non-zero return code: {ret}\n'.format(
ret=status.returncode)
return True, ''
class ReturnCodeIsNonZero(SpirvTest):
"""Mixin class for checking that the return code is not zero."""
def check_return_code_is_nonzero(self, status):
if not status.returncode:
return False, 'return code is 0'
return True, ''
class NoOutputOnStdout(SpirvTest):
"""Mixin class for checking that there is no output on stdout."""
def check_no_output_on_stdout(self, status):
if status.stdout:
return False, 'Non empty stdout: {out}\n'.format(out=status.stdout)
return True, ''
class NoOutputOnStderr(SpirvTest):
"""Mixin class for checking that there is no output on stderr."""
def check_no_output_on_stderr(self, status):
if status.stderr:
return False, 'Non empty stderr: {err}\n'.format(err=status.stderr)
return True, ''
class SuccessfulReturn(ReturnCodeIsZero, NoOutputOnStdout, NoOutputOnStderr):
"""Mixin class for checking that return code is zero and no output on
stdout and stderr."""
pass
class NoGeneratedFiles(SpirvTest):
"""Mixin class for checking that there is no file generated."""
def check_no_generated_files(self, status):
all_files = os.listdir(status.directory)
input_files = status.input_filenames
if all([f.startswith(status.directory) for f in input_files]):
all_files = [os.path.join(status.directory, f) for f in all_files]
generated_files = set(all_files) - set(input_files)
if len(generated_files) == 0:
return True, ''
else:
return False, 'Extra files generated: {}'.format(generated_files)
class CorrectBinaryLengthAndPreamble(SpirvTest):
"""Provides methods for verifying preamble for a SPIR-V binary."""
def verify_binary_length_and_header(self, binary, spv_version=0x10000):
"""Checks that the given SPIR-V binary has valid length and header.
Returns:
False, error string if anything is invalid
True, '' otherwise
Args:
binary: a bytes object containing the SPIR-V binary
spv_version: target SPIR-V version number, with same encoding
as the version word in a SPIR-V header.
"""
def read_word(binary, index, little_endian):
"""Reads the index-th word from the given binary file."""
word = binary[index * 4:(index + 1) * 4]
if little_endian:
word = reversed(word)
return functools.reduce(lambda w, b: (w << 8) | b, word, 0)
def check_endianness(binary):
"""Checks the endianness of the given SPIR-V binary.
Returns:
True if it's little endian, False if it's big endian.
None if magic number is wrong.
"""
first_word = read_word(binary, 0, True)
if first_word == 0x07230203:
return True
first_word = read_word(binary, 0, False)
if first_word == 0x07230203:
return False
return None
num_bytes = len(binary)
if num_bytes % 4 != 0:
return False, ('Incorrect SPV binary: size should be a multiple'
' of words')
if num_bytes < 20:
return False, 'Incorrect SPV binary: size less than 5 words'
preamble = binary[0:19]
little_endian = check_endianness(preamble)
# SPIR-V module magic number
if little_endian is None:
return False, 'Incorrect SPV binary: wrong magic number'
# SPIR-V version number
version = read_word(preamble, 1, little_endian)
# TODO(dneto): Recent Glslang uses version word 0 for opengl_compat
# profile
if version != spv_version and version != 0:
return False, 'Incorrect SPV binary: wrong version number: ' + hex(version) + ' expected ' + hex(spv_version)
# Shaderc-over-Glslang (0x000d....) or
# SPIRV-Tools (0x0007....) generator number
if read_word(preamble, 2, little_endian) != 0x000d0007 and \
read_word(preamble, 2, little_endian) != 0x00070000:
return False, ('Incorrect SPV binary: wrong generator magic ' 'number')
# reserved for instruction schema
if read_word(preamble, 4, little_endian) != 0:
return False, 'Incorrect SPV binary: the 5th byte should be 0'
return True, ''
class CorrectObjectFilePreamble(CorrectBinaryLengthAndPreamble):
"""Provides methods for verifying preamble for a SPV object file."""
def verify_object_file_preamble(self,
filename,
spv_version=DEFAULT_SPIRV_VERSION):
"""Checks that the given SPIR-V binary file has correct preamble."""
success, message = verify_file_non_empty(filename)
if not success:
return False, message
with open(filename, 'rb') as object_file:
object_file.seek(0, os.SEEK_END)
num_bytes = object_file.tell()
object_file.seek(0)
binary = bytes(object_file.read())
return self.verify_binary_length_and_header(binary, spv_version)
return True, ''
class CorrectAssemblyFilePreamble(SpirvTest):
"""Provides methods for verifying preamble for a SPV assembly file."""
def verify_assembly_file_preamble(self, filename):
success, message = verify_file_non_empty(filename)
if not success:
return False, message
with open(filename) as assembly_file:
line1 = assembly_file.readline()
line2 = assembly_file.readline()
line3 = assembly_file.readline()
if (line1 != '; SPIR-V\n' or line2 != '; Version: 1.0\n' or
(not line3.startswith('; Generator: Google Shaderc over Glslang;'))):
return False, 'Incorrect SPV assembly'
return True, ''
class ValidObjectFile(SuccessfulReturn, CorrectObjectFilePreamble):
"""Mixin class for checking that every input file generates a valid SPIR-V 1.0
object file following the object file naming rule, and there is no output on
stdout/stderr."""
def check_object_file_preamble(self, status):
for input_filename in status.input_filenames:
object_filename = get_object_filename(input_filename)
success, message = self.verify_object_file_preamble(
os.path.join(status.directory, object_filename))
if not success:
return False, message
return True, ''
class ValidObjectFile1_3(ReturnCodeIsZero, CorrectObjectFilePreamble):
"""Mixin class for checking that every input file generates a valid SPIR-V 1.3
object file following the object file naming rule, and there is no output on
stdout/stderr."""
def check_object_file_preamble(self, status):
for input_filename in status.input_filenames:
object_filename = get_object_filename(input_filename)
success, message = self.verify_object_file_preamble(
os.path.join(status.directory, object_filename), 0x10300)
if not success:
return False, message
return True, ''
class ValidObjectFile1_5(ReturnCodeIsZero, CorrectObjectFilePreamble):
"""Mixin class for checking that every input file generates a valid SPIR-V 1.5
object file following the object file naming rule, and there is no output on
stdout/stderr."""
def check_object_file_preamble(self, status):
for input_filename in status.input_filenames:
object_filename = get_object_filename(input_filename)
success, message = self.verify_object_file_preamble(
os.path.join(status.directory, object_filename), 0x10500)
if not success:
return False, message
return True, ''
class ValidObjectFile1_6(ReturnCodeIsZero, CorrectObjectFilePreamble):
"""Mixin class for checking that every input file generates a valid SPIR-V 1.6
object file following the object file naming rule, and there is no output on
stdout/stderr."""
def check_object_file_preamble(self, status):
for input_filename in status.input_filenames:
object_filename = get_object_filename(input_filename)
success, message = self.verify_object_file_preamble(
os.path.join(status.directory, object_filename), 0x10600)
if not success:
return False, message
return True, ''
class ValidObjectFileWithAssemblySubstr(SuccessfulReturn,
CorrectObjectFilePreamble):
"""Mixin class for checking that every input file generates a valid object
file following the object file naming rule, there is no output on
stdout/stderr, and the disassmbly contains a specified substring per
input.
"""
def check_object_file_disassembly(self, status):
for an_input in status.inputs:
object_filename = get_object_filename(an_input.filename)
obj_file = str(os.path.join(status.directory, object_filename))
success, message = self.verify_object_file_preamble(obj_file)
if not success:
return False, message
cmd = [status.test_manager.disassembler_path, '--no-color', obj_file]
process = subprocess.Popen(
args=cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=status.directory)
output = process.communicate(None)
disassembly = output[0]
if not isinstance(an_input.assembly_substr, str):
return False, 'Missing assembly_substr member'
if an_input.assembly_substr not in disassembly:
return False, ('Incorrect disassembly output:\n{asm}\n'
'Expected substring not found:\n{exp}'.format(
asm=disassembly, exp=an_input.assembly_substr))
return True, ''
class ValidNamedObjectFile(SuccessfulReturn, CorrectObjectFilePreamble):
"""Mixin class for checking that a list of object files with the given
names are correctly generated, and there is no output on stdout/stderr.
To mix in this class, subclasses need to provide expected_object_filenames
as the expected object filenames.
"""
def check_object_file_preamble(self, status):
for object_filename in self.expected_object_filenames:
success, message = self.verify_object_file_preamble(
os.path.join(status.directory, object_filename))
if not success:
return False, message
return True, ''
class ValidFileContents(SpirvTest):
"""Mixin class to test that a specific file contains specific text
To mix in this class, subclasses need to provide expected_file_contents as
the contents of the file and target_filename to determine the location."""
def check_file(self, status):
target_filename = os.path.join(status.directory, self.target_filename)
if not os.path.isfile(target_filename):
return False, 'Cannot find file: ' + target_filename
with open(target_filename, 'r') as target_file:
file_contents = target_file.read()
if isinstance(self.expected_file_contents, str):
if file_contents == self.expected_file_contents:
return True, ''
return False, ('Incorrect file output: \n{act}\n'
'Expected:\n{exp}'
'With diff:\n{diff}'.format(
act=file_contents,
exp=self.expected_file_contents,
diff='\n'.join(
list(
difflib.unified_diff(
self.expected_file_contents.split('\n'),
file_contents.split('\n'),
fromfile='expected_output',
tofile='actual_output')))))
elif isinstance(self.expected_file_contents, type(re.compile(''))):
if self.expected_file_contents.search(file_contents):
return True, ''
return False, ('Incorrect file output: \n{act}\n'
'Expected matching regex pattern:\n{exp}'.format(
act=file_contents,
exp=self.expected_file_contents.pattern))
return False, (
'Could not open target file ' + target_filename + ' for reading')
class ValidAssemblyFile(SuccessfulReturn, CorrectAssemblyFilePreamble):
"""Mixin class for checking that every input file generates a valid assembly
file following the assembly file naming rule, and there is no output on
stdout/stderr."""
def check_assembly_file_preamble(self, status):
for input_filename in status.input_filenames:
assembly_filename = get_assembly_filename(input_filename)
success, message = self.verify_assembly_file_preamble(
os.path.join(status.directory, assembly_filename))
if not success:
return False, message
return True, ''
class ValidAssemblyFileWithSubstr(ValidAssemblyFile):
"""Mixin class for checking that every input file generates a valid assembly
file following the assembly file naming rule, there is no output on
stdout/stderr, and all assembly files have the given substring specified
by expected_assembly_substr.
To mix in this class, subclasses need to provde expected_assembly_substr
as the expected substring.
"""
def check_assembly_with_substr(self, status):
for input_filename in status.input_filenames:
assembly_filename = get_assembly_filename(input_filename)
success, message = self.verify_assembly_file_preamble(
os.path.join(status.directory, assembly_filename))
if not success:
return False, message
with open(assembly_filename, 'r') as f:
content = f.read()
if self.expected_assembly_substr not in convert_to_unix_line_endings(
content):
return False, ('Incorrect assembly output:\n{asm}\n'
'Expected substring not found:\n{exp}'.format(
asm=content, exp=self.expected_assembly_substr))
return True, ''
class ValidAssemblyFileWithoutSubstr(ValidAssemblyFile):
"""Mixin class for checking that every input file generates a valid assembly
file following the assembly file naming rule, there is no output on
stdout/stderr, and no assembly files have the given substring specified
by unexpected_assembly_substr.
To mix in this class, subclasses need to provde unexpected_assembly_substr
as the substring we expect not to see.
"""
def check_assembly_for_substr(self, status):
for input_filename in status.input_filenames:
assembly_filename = get_assembly_filename(input_filename)
success, message = self.verify_assembly_file_preamble(
os.path.join(status.directory, assembly_filename))
if not success:
return False, message
with open(assembly_filename, 'r') as f:
content = f.read()
if self.unexpected_assembly_substr in convert_to_unix_line_endings(
content):
return False, ('Incorrect assembly output:\n{asm}\n'
'Unexpected substring found:\n{unexp}'.format(
asm=content, exp=self.unexpected_assembly_substr))
return True, ''
class ValidNamedAssemblyFile(SuccessfulReturn, CorrectAssemblyFilePreamble):
"""Mixin class for checking that a list of assembly files with the given
names are correctly generated, and there is no output on stdout/stderr.
To mix in this class, subclasses need to provide expected_assembly_filenames
as the expected assembly filenames.
"""
def check_object_file_preamble(self, status):
for assembly_filename in self.expected_assembly_filenames:
success, message = self.verify_assembly_file_preamble(
os.path.join(status.directory, assembly_filename))
if not success:
return False, message
return True, ''
class ErrorMessage(SpirvTest):
"""Mixin class for tests that fail with a specific error message.
To mix in this class, subclasses need to provide expected_error as the
expected error message.
The test should fail if the subprocess was terminated by a signal.
"""
def check_has_error_message(self, status):
if not status.returncode:
return False, ('Expected error message, but returned success from '
'command execution')
if status.returncode < 0:
# On Unix, a negative value -N for Popen.returncode indicates
# termination by signal N.
# https://docs.python.org/2/library/subprocess.html
return False, ('Expected error message, but command was terminated by '
'signal ' + str(status.returncode))
if not status.stderr:
return False, 'Expected error message, but no output on stderr'
if self.expected_error != convert_to_unix_line_endings(status.stderr):
return False, ('Incorrect stderr output:\n{act}\n'
'Expected:\n{exp}'.format(
act=status.stderr, exp=self.expected_error))
return True, ''
class ErrorMessageSubstr(SpirvTest):
"""Mixin class for tests that fail with a specific substring in the error
message.
To mix in this class, subclasses need to provide expected_error_substr as
the expected error message substring.
The test should fail if the subprocess was terminated by a signal.
"""
def check_has_error_message_as_substring(self, status):
if not status.returncode:
return False, ('Expected error message, but returned success from '
'command execution')
if status.returncode < 0:
# On Unix, a negative value -N for Popen.returncode indicates
# termination by signal N.
# https://docs.python.org/2/library/subprocess.html
return False, ('Expected error message, but command was terminated by '
'signal ' + str(status.returncode))
if not status.stderr:
return False, 'Expected error message, but no output on stderr'
if self.expected_error_substr not in convert_to_unix_line_endings(
status.stderr):
return False, ('Incorrect stderr output:\n{act}\n'
'Expected substring not found in stderr:\n{exp}'.format(
act=status.stderr, exp=self.expected_error_substr))
return True, ''
class WarningMessage(SpirvTest):
"""Mixin class for tests that succeed but have a specific warning message.
To mix in this class, subclasses need to provide expected_warning as the
expected warning message.
"""
def check_has_warning_message(self, status):
if status.returncode:
return False, ('Expected warning message, but returned failure from'
' command execution')
if not status.stderr:
return False, 'Expected warning message, but no output on stderr'
if self.expected_warning != convert_to_unix_line_endings(status.stderr):
return False, ('Incorrect stderr output:\n{act}\n'
'Expected:\n{exp}'.format(
act=status.stderr, exp=self.expected_warning))
return True, ''
class ValidObjectFileWithWarning(NoOutputOnStdout, CorrectObjectFilePreamble,
WarningMessage):
"""Mixin class for checking that every input file generates a valid object
file following the object file naming rule, with a specific warning message.
"""
def check_object_file_preamble(self, status):
for input_filename in status.input_filenames:
object_filename = get_object_filename(input_filename)
success, message = self.verify_object_file_preamble(
os.path.join(status.directory, object_filename))
if not success:
return False, message
return True, ''
class ValidAssemblyFileWithWarning(NoOutputOnStdout,
CorrectAssemblyFilePreamble, WarningMessage):
"""Mixin class for checking that every input file generates a valid assembly
file following the assembly file naming rule, with a specific warning
message."""
def check_assembly_file_preamble(self, status):
for input_filename in status.input_filenames:
assembly_filename = get_assembly_filename(input_filename)
success, message = self.verify_assembly_file_preamble(
os.path.join(status.directory, assembly_filename))
if not success:
return False, message
return True, ''
class StdoutMatch(SpirvTest):
"""Mixin class for tests that can expect output on stdout.
To mix in this class, subclasses need to provide expected_stdout as the
expected stdout output.
For expected_stdout, if it's True, then they expect something on stdout but
will not check what it is. If it's a string, expect an exact match. If it's
anything else, it is assumed to be a compiled regular expression which will
be matched against re.search(). It will expect
expected_stdout.search(status.stdout) to be true.
"""
def check_stdout_match(self, status):
# "True" in this case means we expect something on stdout, but we do not
# care what it is, we want to distinguish this from "blah" which means we
# expect exactly the string "blah".
if self.expected_stdout is True:
if not status.stdout:
return False, 'Expected something on stdout'
elif type(self.expected_stdout) == str:
if self.expected_stdout != convert_to_unix_line_endings(status.stdout):
return False, ('Incorrect stdout output:\n{ac}\n'
'Expected:\n{ex}'.format(
ac=status.stdout, ex=self.expected_stdout))
else:
converted = convert_to_unix_line_endings(status.stdout)
if not self.expected_stdout.search(converted):
return False, ('Incorrect stdout output:\n{ac}\n'
'Expected to match regex:\n{ex}'.format(
ac=status.stdout, ex=self.expected_stdout.pattern))
return True, ''
class StderrMatch(SpirvTest):
"""Mixin class for tests that can expect output on stderr.
To mix in this class, subclasses need to provide expected_stderr as the
expected stderr output.
For expected_stderr, if it's True, then they expect something on stderr,
but will not check what it is. If it's a string, expect an exact match.
If it's anything else, it is assumed to be a compiled regular expression
which will be matched against re.search(). It will expect
expected_stderr.search(status.stderr) to be true.
"""
def check_stderr_match(self, status):
# "True" in this case means we expect something on stderr, but we do not
# care what it is, we want to distinguish this from "blah" which means we
# expect exactly the string "blah".
if self.expected_stderr is True:
if not status.stderr:
return False, 'Expected something on stderr'
elif type(self.expected_stderr) == str:
if self.expected_stderr != convert_to_unix_line_endings(status.stderr):
return False, ('Incorrect stderr output:\n{ac}\n'
'Expected:\n{ex}'.format(
ac=status.stderr, ex=self.expected_stderr))
else:
if not self.expected_stderr.search(
convert_to_unix_line_endings(status.stderr)):
return False, ('Incorrect stderr output:\n{ac}\n'
'Expected to match regex:\n{ex}'.format(
ac=status.stderr, ex=self.expected_stderr.pattern))
return True, ''
class StdoutNoWiderThan80Columns(SpirvTest):
"""Mixin class for tests that require stdout to 80 characters or narrower.
To mix in this class, subclasses need to provide expected_stdout as the
expected stdout output.
"""
def check_stdout_not_too_wide(self, status):
if not status.stdout:
return True, ''
else:
for line in status.stdout.splitlines():
if len(line) > 80:
return False, ('Stdout line longer than 80 columns: %s' % line)
return True, ''
class NoObjectFile(SpirvTest):
"""Mixin class for checking that no input file has a corresponding object
file."""
def check_no_object_file(self, status):
for input_filename in status.input_filenames:
object_filename = get_object_filename(input_filename)
full_object_file = os.path.join(status.directory, object_filename)
print('checking %s' % full_object_file)
if os.path.isfile(full_object_file):
return False, (
'Expected no object file, but found: %s' % full_object_file)
return True, ''
class NoNamedOutputFiles(SpirvTest):
"""Mixin class for checking that no specified output files exist.
The expected_output_filenames member should be full pathnames."""
def check_no_named_output_files(self, status):
for object_filename in self.expected_output_filenames:
if os.path.isfile(object_filename):
return False, (
'Expected no output file, but found: %s' % object_filename)
return True, ''
class ExecutedListOfPasses(SpirvTest):
"""Mixin class for checking that a list of passes where executed.
It works by analyzing the output of the --print-all flag to spirv-opt.
For this mixin to work, the class member expected_passes should be a sequence
of pass names as returned by Pass::name().
"""
def check_list_of_executed_passes(self, status):
# Collect all the output lines containing a pass name.
pass_names = []
pass_name_re = re.compile(r'.*IR before pass (?P<pass_name>[\S]+)')
for line in status.stderr.splitlines():
match = pass_name_re.match(line)
if match:
pass_names.append(match.group('pass_name'))
for (expected, actual) in zip(self.expected_passes, pass_names):
if expected != actual:
return False, (
'Expected pass "%s" but found pass "%s"\n' % (expected, actual))
return True, ''
|