def test_multi_level_descent_and_ascent_works(self): str_input = """ page QWidget vlayout QVBoxLayout a QLabel b QLabel fred QWidget hlayout QHBoxLayout lbl QLabel c QLabel """ layouts_created = Builder.build(str_input, 'unit test provenenance') dumped = MultilineString.normalise(layouts_created.dump()) expected = MultilineString.normalise(""" page QWidget page.vlayout QVBoxLayout page.vlayout.a QLabel page.vlayout.b QLabel page.vlayout.fred QWidget page.vlayout.fred.hlayout QHBoxLayout page.vlayout.fred.hlayout.lbl QLabel page.vlayout.c QLabel """) self.assertEqual(dumped, expected) layout = layouts_created.at('vlayout') self.assertEqual(layout.count(), 4)
def test_remove_empty_first_and_last_lines(self): # Normal usage result = MultilineString.remove_empty_first_and_last_lines(""" foo bar """) self.assertTrue(result.startswith(' foo')) self.assertTrue(result.endswith('bar')) # When neither first or last line is empty. result = MultilineString.remove_empty_first_and_last_lines("""x foo bar x""") self.assertTrue(result.startswith('x\n')) self.assertTrue(result.endswith('x')) # When input is empty. result = MultilineString.remove_empty_first_and_last_lines('') self.assertEqual(result, '') # When several lines exist at the beginning and / or end that are # whitespace only. result = MultilineString.remove_empty_first_and_last_lines(""" foo bar """) self.assertEqual(result, ' foo\n bar')
def test_add_backup_location_comment(self): # First check that we get what we expect when the existing # one_big_string, does not already have such a comment in. one_big_string = 'just this text' mock_backup_folder_string = 'mock_backup_folder' output = OriginalFileReWriter._add_backup_location_comment( mock_backup_folder_string, one_big_string) output = MultilineString.normalise(output) expected = MultilineString.normalise(""" # This file has been automatically re-formatted. # Previous versions can be found here: # mock_backup_folder ## just this text """) self.assertEquals(output, expected) # Now ensure that if we do it again - but this time with the # new one_big_string that already has a comment in, the old comment # gets replaced with the new. previous_output = output mock_backup_folder_string = 'DIFFERENT_mock_backup_folder' new_output = \ OriginalFileReWriter._add_backup_location_comment( mock_backup_folder_string, previous_output) new_output = MultilineString.normalise(new_output) expected = MultilineString.normalise(""" # This file has been automatically re-formatted. # Previous versions can be found here: # DIFFERENT_mock_backup_folder ## just this text """) self.assertEquals(new_output, expected)
def test_sibling_child_additions_work(self): str_input = """ layout QVBoxLayout a QLabel b QLabel c QLabel """ layouts_created = Builder.build(str_input, 'unit test provenenance') MultilineString.normalise(layouts_created.dump()) layout = layouts_created.at('layout') self.assertEqual(layout.count(), 3)
def test_simplest_possible_dump_of_contents_is_correct(self): str_input = """ page QWidget layout QVBoxLayout """ layouts_created = Builder.build(str_input, 'unit test provenenance') dumped = MultilineString.normalise(layouts_created.dump()) expected = MultilineString.normalise(""" page QWidget page.layout QVBoxLayout """) self.assertEqual(dumped, expected)
def format(cls, one_big_string): lines = MultilineString.get_as_left_shifted_lines(one_big_string) parsed_lines = [LineParser.parse_line(line) for line in lines] # First pass is done only to measure the longest (indent + name) # section present. widest = -1 for parsed_line in parsed_lines: is_a_comment, is_blank, indent, name, type_string, parenthesised = \ parsed_line if is_a_comment or is_blank: continue extent = indent + len(name) if extent > widest: widest = extent # Second pass reconstitutes the output with the padding necessary # to create alignment. formatted_lines = [] for parsed_line, line in zip(parsed_lines, lines): is_a_comment, is_blank, indent, name, type_string, parenthesised = \ parsed_line if is_a_comment or is_blank: formatted_lines.append(line) continue padding_required = widest + cls._MIN_GUTTER - (indent + len(name)) output_line = '' output_line += ' ' * indent output_line += name output_line += ' ' * padding_required output_line += type_string if parenthesised: output_line += '(%s)' % parenthesised formatted_lines.append(output_line) return '\n'.join(formatted_lines)
def test_it(self): # The right column justification of this input is delibarately wild. str_input = """ my_page QWidget layout QHBoxLayout label QLabel(hello) """ output = ReFormatter.format(str_input) output_for_comparison = MultilineString.get_as_left_shifted_lines( output) expected = MultilineString.get_as_left_shifted_lines(""" my_page QWidget layout QHBoxLayout label QLabel(hello) """) self.assertEqual(output_for_comparison, expected)
def _attempt_build(self): try: users_layouts = build_from_file( self._input_path, auto_format_and_overwrite=False) except LayoutError as e: if 'Cannot read this file' in str(e): self._log.setText(MultilineString.shift_left(""" The builder says it cannot access your input file, but that is probably because your editor had it locked at the moment it tried. It will carry on as normal the next time you save a change. """)) else: self._log.setText(str(e)) return top_item = users_layouts.first_top_level_item() # If the top level item in the tree is a widget, we just show it. if isinstance(top_item, QWidget): self._show_built_content(top_item) # Whereas, if it is a layout we wrap it in a widget so we can show it. elif isinstance(top_item, QLayout): wrapper = QWidget(top_item) self._show_built_content(wrapper) self._log.setText('Build successful')
def test_more_than_one_top_level_object_works(self): str_input = """ page1 QWidget layout1 QVBoxLayout page2 QWidget layout2 QVBoxLayout """ layouts_created = Builder.build(str_input, 'unit test provenenance') dumped = MultilineString.normalise(layouts_created.dump()) expected = MultilineString.normalise(""" page1 QWidget page1.layout1 QVBoxLayout page2 QWidget page2.layout2 QVBoxLayout """) self.assertEqual(dumped, expected) widget = layouts_created.at('page2') self.assertTrue(isinstance(widget.layout(), QVBoxLayout))
def test_multi_level_descent_works(self): str_input = """ page QWidget layout QVBoxLayout a QLabel b QLabel c QLabel """ layouts_created = Builder.build(str_input, 'unit test provenenance') dumped = MultilineString.normalise(layouts_created.dump()) expected = MultilineString.normalise(""" page QWidget page.layout QVBoxLayout page.layout.a QLabel page.layout.b QLabel page.layout.c QLabel """) self.assertEqual(dumped, expected)
def test_normalise(self): str_input = """ foo bar baz """ result = MultilineString.normalise(str_input) lines = result.split('\n') self.assertEqual(lines[0], 'foo') self.assertEqual(lines[1], 'bar') self.assertEqual(lines[2], 'baz')
def test_shift_left(self): # Normal usage str_input = """ foo bar baz """ result = MultilineString.shift_left(str_input) lines = result.split('\n') self.assertEqual(lines[0], 'foo') self.assertEqual(lines[1], ' bar') self.assertEqual(lines[2], ' baz')
def raises_layout_error_with_this_message(required_message, this_callable, *args, **kwargs): """ Makes sure that the callable object provided, when called with *args and **kwargs, raises a LayoutException, and that the exception's string representation matches that specified in required_message. The message equality checking copes with multiline strings and massages these before comparison to unify minor differences in whitespace and indentation. """ try: this_callable(*args, **kwargs) # If reach here, it didn't raise the exception. return False except LayoutError as e: error_msg = MultilineString.normalise(str(e)) required_message = MultilineString.normalise(required_message) if error_msg == required_message: return True # If we reach here, the message produces does not match that required. print 'Message produced was\n%s' % error_msg return False
def _add_backup_location_comment(cls, backup_folder, one_big_string): comment_string = """ # This file has been automatically re-formatted. # Previous versions can be found here: # %s ## """ % backup_folder comment_string = MultilineString.normalise(comment_string) if cls._BACKUP_COMMENT_RE.search(one_big_string): one_big_string = cls._BACKUP_COMMENT_RE.sub( comment_string, one_big_string, 1) else: one_big_string = comment_string + '\n' + one_big_string return one_big_string
def build(cls, one_big_string, provenance): # Construct the widget and layout finder helper just once and early # on because its construction is expensive. finder = WidgetAndLayoutFinder() layouts_created = LayoutsCreated() # Will be populated, then returned. line_number = 0 lines = MultilineString.get_as_left_shifted_lines(one_big_string) for line in lines: line_number += 1 cls._process_line(line, finder, layouts_created, line_number, provenance) BuilderAssertions.assert_layouts_created_is_not_empty( layouts_created, provenance) return layouts_created
def raises_layout_error_with_this_approximately_this_message( required_message, this_callable, *args, **kwargs): """ Identical to raises_layout_error_with_this_message, except that it ignores ALL whitespace in both the required_message and the exception report. This is for when you can't easily express the required message in a test because part of the string is too long to comply with PEP8. Like long exception messages from python that are being re-reported. """ try: this_callable(*args, **kwargs) # If reach here, it didn't raise the exception. return False except LayoutError as e: error_msg = MultilineString.normalise(str(e)) error_msg = string_utils.with_all_whitespace_removed(error_msg) required_message = \ string_utils.with_all_whitespace_removed( MultilineString.normalise(required_message)) if error_msg == required_message: return True # If we reach here, the message produces does not match that required. print 'Message produced was\n%s' % error_msg return False
def test_reformatted_file_gets_written_to_file_specified(self): tmp_dir = tempfile.mkdtemp() reformat_location = path.join(tmp_dir, 're-formatted.txt') str_input = """ top_widget QWidget layout QVBoxLayout """ build_from_multi_line_string( str_input, auto_format_and_write_to=reformat_location) with open(reformat_location, 'r') as input_file: contents = MultilineString.shift_left(input_file.read()) print contents self.assertEqual( contents, MultilineString.shift_left(""" top_widget QWidget layout QVBoxLayout """)) shutil.rmtree(tmp_dir)
def test_at_api_level(self): # Make a file that we will then overwrite. orig_fd = tempfile.NamedTemporaryFile(suffix='.txt', delete=False) orig_file_path = orig_fd.name content = MultilineString.shift_left(""" layout QHBoxLayout widget QWidget """) orig_fd.write(content) orig_fd.close() # Mandate the overwrite OriginalFileReWriter.overwrite_original(orig_file_path, 'new content') # Check for both the presence of the new content, and the # backup message. with open(orig_file_path, 'r') as input_file: content = input_file.read() self.assertTrue('new content' in content) self.assertTrue('has been' in content)
def __init__(self, multiline_format_string, args): str_format = MultilineString.normalise(multiline_format_string) message = str_format % args super(Exception, self).__init__(message)