Skip to content

Commit

Permalink
Add 3c-regtest.py and migrate one example test to use it.
Browse files Browse the repository at this point in the history
  • Loading branch information
mattmccutchen-cci committed Dec 11, 2020
1 parent 9a8a7b2 commit 41740eb
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 6 deletions.
101 changes: 101 additions & 0 deletions clang/test/3C/3c-regtest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/usr/bin/env python
#
# Usage: 3c-regtest -t TMPNAME [options...] SRC_FILE
#
# TMPNAME corresponds to %t and SRC_FILE corresponds to %s. Both are required.
#
# --subst can be used for things such as %clang, for example (assuming single
# --quotes are removed by the shell):
#
# --subst %clang 'clang -some-flag'
#
# (Note: A literal % has to be represented as %% in a RUN line. If we instead
# established the convention of automatically prepending the % here, then the
# RUN line would trip the "Do not use 'clang' in tests, use '%clang'." error.)
#
# Example RUN line:
#
# // RUN: %S/3c-regtest.py -t %t --subst %%clang '%clang' %s
#
# Soon, we'll add options for different kinds of 3C regression tests.

# TODO: Add Windows compatibility code once we have an easy way to test on Windows.

import sys
import os
import platform
import argparse

sys.path.insert(0, os.path.dirname(__file__) + '/../../../llvm/utils/lit')

This comment has been minimized.

Copy link
@mwhicks1

mwhicks1 Dec 12, 2020

Member

What is __file__ in this context (I'm not a Python programmer)?

Are we confident that this relative path makes sense in all contexts?

This comment has been minimized.

Copy link
@mwhicks1

mwhicks1 Dec 12, 2020

Member

Maybe there's a way to look at the path of the lit command that ended up invoking this script via RUN ?

This comment has been minimized.

Copy link
@mattmccutchen-cci

mattmccutchen-cci Dec 14, 2020

Author Member

__file__ is a built-in variable containing the path to the file containing it. In some cases, the path may be relative to the working directory, and I realize we should make it absolute here to be safe; I've done that in #355. Once we do that, the dirname removes the 3c-regtest.py, so appending /../../../llvm/utils/lit should always get us to the lit directory.

import lit.TestRunner

This comment has been minimized.

Copy link
@mwhicks1

mwhicks1 Dec 12, 2020

Member

Where did the lit variable come from? This is you reinvoking lit from within this script I take it?

This comment has been minimized.

Copy link
@mattmccutchen-cci

mattmccutchen-cci Dec 14, 2020

Author Member

Yes. import lit.TestRunner loads lit/TestRunner.py from the directory I added to sys.path above, then it automatically creates an intermediate lit object and sets lit.TestRunner to the object loaded from lit/TestRunner.py.


print "NOTICE: cwd is %s" % os.getcwd()

def die(msg):
sys.stderr.write('Error: %s\n' % msg)
sys.exit(1)

parser = argparse.ArgumentParser(description='Run a 3C regression test.')
# TODO: Add help
parser.add_argument('test_file')
parser.add_argument('-t', required=True)
parser.add_argument('--subst', action='append', nargs=2, default=[])
args = parser.parse_args()

test_dir = os.path.dirname(args.test_file)
if test_dir == '':
test_dir = '.'

tmpName = args.t
tmpNameSuffix = '.tmp'
if tmpName.endswith(tmpNameSuffix):
tmpBase = tmpName[:-len(tmpNameSuffix)]
else:
die('-t argument %s does not end with %s' % (tmpName, tmpNameSuffix))

substitutions = [
('%%', '#_MARKER_#'),

This comment has been minimized.

Copy link
@mwhicks1

mwhicks1 Dec 12, 2020

Member

What is #_MARKER_# ?

This comment has been minimized.

Copy link
@mattmccutchen-cci

mattmccutchen-cci Dec 14, 2020

Author Member

Added a comment in the new PR (#355).

('%s', args.test_file),
('%S', test_dir),
('%t', tmpName),
]
substitutions.extend(args.subst)
substitutions.append(('#_MARKER_#', '%'))

# Starting with processor.py because it's always the same.
commands = [
# FIXME: 'foo.c' + 'hecked.c' is a terrible hack; find the right way to do this.
'3c -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s',
'3c -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s',
'3c -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null -',
'3c -output-postfix=checked -alltypes %s',
'3c -alltypes %shecked.c -- | count 0',
'rm %shecked.c',
]
commands = lit.TestRunner.applySubstitutions(commands, substitutions)

class FakeTestConfig:
def __init__(self):
self.pipefail = True # Is this always OK?
self.environment = dict(os.environ)

class FakeTest:
def __init__(self):
self.config = FakeTestConfig()

class FakeLitConfig:
def __init__(self):
self.isWindows = platform.system() == 'windows'
# Let the calling `lit` handle any timeout.
self.maxIndividualTestTime = 0

res = lit.TestRunner.executeScriptInternal(
FakeTest(), FakeLitConfig(), tmpBase, commands, os.getcwd())
if isinstance(res, lit.Test.Result):
die('Error: executeScriptInternal returned unexpected Result(%s, %r)' %
(res.code.name, res.output))

out, err, exitCode, timeoutInfo = res
sys.stdout.write(out)
sys.stderr.write(err)
sys.exit(exitCode)
7 changes: 1 addition & 6 deletions clang/test/3C/b9_allsafestructp.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
// RUN: 3c -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s
// RUN: 3c -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s
// RUN: 3c -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null -
// RUN: 3c -output-postfix=checked -alltypes %s
// RUN: 3c -alltypes %S/b9_allsafestructp.checked.c -- | count 0
// RUN: rm %S/b9_allsafestructp.checked.c

This comment has been minimized.

Copy link
@kyleheadley

kyleheadley Dec 11, 2020

Member

This list of RUN commands does 3 things, the first 2 of which require annotations in the body of the code. 1)Check against annotations for 3c with -alltypes and -addcr. Fail to followup with clang. 2) Check against annotations for 3c with -addcr. Follow up with clang 3) check for idempotence when running 3c with -alltypes. Fail to followup with clang.

The steps seem independent to me, and could be expanded. More clang follow-ups. More permutations of tests and 3c options. Not that all of these would be useful. And many require user annotations, the CHECK lines below.

This comment has been minimized.

Copy link
@mattmccutchen-cci

mattmccutchen-cci Dec 11, 2020

Author Member

Yes, I will expand 3c-regtest to do a lot more; this is just a first demo of the approach of calling lit under lit, using one of the processor.py tests as an example. The CHECK annotations in the file are picked up by FileCheck as before, but this time running via 3c-regtest.

// RUN: %S/3c-regtest.py -t %t --subst %%clang '%clang' %s
#include <stddef.h>
#include <stddef.h>
extern _Itype_for_any(T) void *calloc(size_t nmemb, size_t size) : itype(_Array_ptr<T>) byte_count(nmemb * size);
Expand Down

1 comment on commit 41740eb

@mattmccutchen-cci
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've answered all the questions here. Please post any further questions on #355. (I probably should have gone ahead and filed the draft PR, but I wasn't sure yet what I wanted to name the source branch and GitHub doesn't let me change it after creating a PR.)

Please sign in to comment.