Skip to content

Instantly share code, notes, and snippets.

@Anteru
Created November 1, 2017 12:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Anteru/1d40d299458846289da0650c94b34146 to your computer and use it in GitHub Desktop.
Save Anteru/1d40d299458846289da0650c94b34146 to your computer and use it in GitHub Desktop.
import sys
import re
import os
testCaseRegex = re.compile (r'TEST\s*\((\w+),\s*(\w+)\)')
expectRegex = re.compile (r'EXPECT_(\w+)\s*\((.*)\);')
assertRegex = re.compile (r'ASSERT_(\w+)\s*\((.*)\);')
leadingWhitespaceRegex = re.compile(r'^(\s*)')
module = "[unknown]"
def SplitCheck (check):
def IsShiftOrComparison (c, i, check):
if c in {'<', '>'}:
# we can be the second or first character
# look ahead first, could be = as well there
if (i+1) < len (check):
if check [i+1] in {'<', '>', '='}:
return True
# look behind, this is only needed for >> and <<
if i > 0:
if check [i-1] == c:
return True
return False
# we start from the left, increment when we see anything of {, <, (,
# decrement if we see }, >, ), and if we're at level 0 and see a , -- we
# split at that location
nesting = 0
inString = False
for i,c in enumerate (check):
# Handle strings - we ignore everything inside quotes, but we still need
# to check for escaped quotes
# This handles "\"" and also correctly ignores "\\"
if c == '"':
if not inString:
inString = True
else:
# escaped quote inside a string - ignore
if i > 0 and check [i-1] == '\\':
# Check again if the backslash was escaped
if i > 1 and check [i-2] != '\\':
inString = False
else:
continue
inString = False
continue
if inString:
continue
# Next problem case are shift and comparison operators, we might have
# <=, <<, >=, >>, <, >. Unfortunately, we have no hope in figuring out
# whether something like a<b,c> means a<b and c or a<b,c> with b,c being
# template parameters. Thus, we're only going to handle the simple cases
# for now, which are <=, <<, >=, >>
if IsShiftOrComparison (c, i, check):
continue
if c in {'{', '<', '('}:
nesting += 1
elif c in {'}', '>', ')'}:
nesting -= 1
elif c == ',':
if nesting > 0:
continue
else:
return check [:i].strip (), check [i+1:].strip ()
else:
return '/* GTEST-CATCH FIXME */' + check, ''
def ProcessCheck (match, prefix, line):
basicComparisonOps = {
'EQ' : '==',
'NE' : '!=',
'LE' : '>=',
'LT' : '>',
'GT' : '<',
'GE' : '<='}
floatComparisonOps = {
'FLOAT_EQ' : '==',
'FLOAT_NE' : '!=',
'DOUBLE_EQ' : '==',
'DOUBLE_NE' : '!=',
'NEAR' : '=='}
if match [0] == 'TRUE':
return prefix + (' ({});\n'.format (match [1]))
elif match [0] == 'FALSE':
return prefix + (' (!{});\n'.format (match [1]))
elif match [0] in basicComparisonOps:
expected, actual = SplitCheck (match [1])
# LE/LT/GT/GE are turned around as EXPECT_LE(a, b) means a <= b, but we
# want this to become b >= a
op = basicComparisonOps [match[0]]
return prefix + ' ({} {} {});\n'.format (actual, op, expected)
elif match [0] in floatComparisonOps:
expected, actual = SplitCheck (match [1])
op = floatComparisonOps [match [0]]
return prefix + ' ({} {} Approx ({}));\n'.format (actual, op, expected)
elif match [0] == 'THROW':
expected, exception = SplitCheck (match [1])
return prefix + '_THROWS_AS ({}, {});\n'.format (expected, exception)
elif match [0] == 'NO_THROW':
return prefix + '_NOTHROW ({});\n'.format (match [1])
elif match [0] == 'STREQ':
expected, actual = SplitCheck (match [1])
return prefix + '_THAT ({}, Catch::Equals ({}));\n'.format (actual, expected)
else:
raise Exception ('Unknown match: "{}"'.format (line))
def ProcessLine (line):
ws = leadingWhitespaceRegex.match (line).group (0)
match = testCaseRegex.search (line)
if match:
g = match.groups ()
return ws + ('TEST_CASE("{}: {}", "{}")\n'.format (g[0], g[1], module))
match = expectRegex.search (line)
if match:
g = match.groups ()
return ProcessCheck (g, ws + 'CHECK', line)
match = assertRegex.search (line)
if match:
g = match.groups ()
return ProcessCheck (g, ws + 'REQUIRE', line)
return line
if __name__ == '__main__':
module = "[{}]".format (sys.argv[2])
for root, d, files in os.walk (sys.argv [1]):
for f in files:
if f.endswith ('.cpp'):
p = os.path.join (root, f)
lines = open (p, 'r').readlines ()
output = map (ProcessLine, lines)
open (p, 'w').write (''.join (output))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment