Skip to content
Snippets Groups Projects
Commit da4e798e authored by Mike Pennisi's avatar Mike Pennisi
Browse files

[generation] Prevent invalid transformation

In order to promote readability of the generated test material, the test
generation tool may insert whitespace if the context a given expanded
variable calls for it. Avoid inserting such whitespace within literal
values that span multiple lines.
parent 975e54de
No related branches found
No related tags found
No related merge requests found
Showing
with 217 additions and 6 deletions
...@@ -11,18 +11,40 @@ from test import Test ...@@ -11,18 +11,40 @@ from test import Test
indentPattern = re.compile(r'^(\s*)') indentPattern = re.compile(r'^(\s*)')
interpolatePattern = re.compile(r'\{\s*(\S+)\s*\}') interpolatePattern = re.compile(r'\{\s*(\S+)\s*\}')
def indent(text, prefix = ' '): def indent(text, prefix = ' ', js_value = False):
'''Prefix a block of text (as defined by the "line break" control '''Prefix a block of text (as defined by the "line break" control
character) with some character sequence.''' character) with some character sequence.
:param prefix: String value to insert before each line
:param js_value: If True, the text will be interpreted as a JavaScript
value, meaning that indentation will not occur for lines that would
effect the runtime value; defaults to False
'''
if isinstance(text, list): if isinstance(text, list):
lines = text lines = text
else: else:
lines = text.split('\n') lines = text.split('\n')
indented = map( indented = [prefix + lines[0]]
lambda line: line if len(line) == 0 else prefix + line, str_char = None
lines)
for line in lines[1:]:
# Determine if the beginning of the current line is part of some
# previously-opened literal value.
if js_value:
for char in indented[-1]:
if char == str_char:
str_char = None
elif str_char is None and char in '\'"`':
str_char = char
# Do not indent the current line if it is a continuation of a literal
# value or if it is empty.
if str_char or len(line) == 0:
indented.append(line)
else:
indented.append(prefix + line)
return '\n'.join(indented) return '\n'.join(indented)
...@@ -90,7 +112,7 @@ class Template: ...@@ -90,7 +112,7 @@ class Template:
value = value.replace('\n', '\\\n') value = value.replace('\n', '\\\n')
source = source[:region['firstchar']] + \ source = source[:region['firstchar']] + \
indent(value, whitespace).lstrip() + \ indent(value, whitespace, True).lstrip() + \
source[region['lastchar']:] source[region['lastchar']:]
setup = context['regions'].get('setup') setup = context['regions'].get('setup')
......
// This file was procedurally generated from the following sources:
// - tools/generation/test/fixtures/indent-code.case
// - tools/generation/test/fixtures/indentation/spaces.template
/*---
description: Multiple lines of code (Preserving "soft" indentation across newlines)
flags: [generated]
---*/
(function() {
'These literals are each contained on a single line...';
"...which means they may be indented...";
`...without effecting the semantics of the generated source code.`;
if (true) {
'These literals are each contained on a single line...';
"...which means they may be indented...";
`...without effecting the semantics of the generated source code.`;
}
}());
// This file was procedurally generated from the following sources:
// - tools/generation/test/fixtures/indent-string-continuation.case
// - tools/generation/test/fixtures/indentation/spaces.template
/*---
description: Multiline string via a line continuation character (Preserving "soft" indentation across newlines)
flags: [generated]
---*/
(function() {
'this string is declared across multiple lines\
\
which disqualifies it as a candidate for indentation';
if (true) {
'this string is declared across multiple lines\
\
which disqualifies it as a candidate for indentation';
}
}());
// This file was procedurally generated from the following sources:
// - tools/generation/test/fixtures/indent-string-template.case
// - tools/generation/test/fixtures/indentation/spaces.template
/*---
description: String template spanning multiple lines (Preserving "soft" indentation across newlines)
flags: [generated]
---*/
(function() {
`this string template is declared across multiple lines
which disqualifies it as a candidate for indentation
it also happens to contain ' and ".`;
if (true) {
`this string template is declared across multiple lines
which disqualifies it as a candidate for indentation
it also happens to contain ' and ".`;
}
}());
// This file was procedurally generated from the following sources:
// - tools/generation/test/fixtures/indent-code.case
// - tools/generation/test/fixtures/indentation/tabs.template
/*---
description: Multiple lines of code (Preserving "hard" indentation across newlines)
flags: [generated]
---*/
(function() {
'These literals are each contained on a single line...';
"...which means they may be indented...";
`...without effecting the semantics of the generated source code.`;
if (true) {
'These literals are each contained on a single line...';
"...which means they may be indented...";
`...without effecting the semantics of the generated source code.`;
}
}());
// This file was procedurally generated from the following sources:
// - tools/generation/test/fixtures/indent-string-continuation.case
// - tools/generation/test/fixtures/indentation/tabs.template
/*---
description: Multiline string via a line continuation character (Preserving "hard" indentation across newlines)
flags: [generated]
---*/
(function() {
'this string is declared across multiple lines\
\
which disqualifies it as a candidate for indentation';
if (true) {
'this string is declared across multiple lines\
\
which disqualifies it as a candidate for indentation';
}
}());
// This file was procedurally generated from the following sources:
// - tools/generation/test/fixtures/indent-string-template.case
// - tools/generation/test/fixtures/indentation/tabs.template
/*---
description: String template spanning multiple lines (Preserving "hard" indentation across newlines)
flags: [generated]
---*/
(function() {
`this string template is declared across multiple lines
which disqualifies it as a candidate for indentation
it also happens to contain ' and ".`;
if (true) {
`this string template is declared across multiple lines
which disqualifies it as a candidate for indentation
it also happens to contain ' and ".`;
}
}());
// Copyright (C) 2017 Mike Pennisi. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
template: indentation
desc: Multiple lines of code
---*/
//- value
'These literals are each contained on a single line...';
"...which means they may be indented...";
`...without effecting the semantics of the generated source code.`;
// Copyright (C) 2017 Mike Pennisi. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
template: indentation
desc: Multiline string via a line continuation character
---*/
//- value
'this string is declared across multiple lines\
\
which disqualifies it as a candidate for indentation';
// Copyright (C) 2017 Mike Pennisi. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
template: indentation
desc: String template spanning multiple lines
---*/
//- value
`this string template is declared across multiple lines
which disqualifies it as a candidate for indentation
it also happens to contain ' and ".`;
// Copyright (C) 2017 Mike Pennisi. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
name: Preserving "soft" indentation across newlines
path: indentation/spaces-
---*/
(function() {
/*{ value }*/
if (true) {
/*{ value }*/
}
}());
// Copyright (C) 2017 Mike Pennisi. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
name: Preserving "hard" indentation across newlines
path: indentation/tabs-
---*/
(function() {
/*{ value }*/
if (true) {
/*{ value }*/
}
}());
...@@ -59,5 +59,14 @@ class TestGeneration(unittest.TestCase): ...@@ -59,5 +59,14 @@ class TestGeneration(unittest.TestCase):
self.assertEqual(result['returncode'], 0) self.assertEqual(result['returncode'], 0)
self.compareTrees('negative') self.compareTrees('negative')
def test_indentation(self):
result = self.fixture('indent-code.case')
self.assertEqual(result['returncode'], 0)
result = self.fixture('indent-string-continuation.case')
self.assertEqual(result['returncode'], 0)
result = self.fixture('indent-string-template.case')
self.assertEqual(result['returncode'], 0)
self.compareTrees('indentation')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment