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