2 # This file is part of the LibreOffice UI_logger project.
4 # This file contain the implementation of the Compiler
5 # for the new logger grammar
7 # ul stands for Ui_Logger
15 from textx
.metamodel
import metamodel_from_file
17 print("textx is a required package.")
18 print('Please install the package for example with "pip3 install --user textx"')
27 This function parses the command-line arguments
28 to get the input and output file details
30 parser
= argparse
.ArgumentParser(description
="Generate a UI test file from log")
31 parser
.add_argument("input_address", type=str, help="The log file address")
32 parser
.add_argument("output_address", type=str, help="The test file address")
33 args
= parser
.parse_args()
42 parent_hierarchy_count
= 0
44 flag_for_QuerySaveDialog
= False
46 def __init__(self
, input_address
, output_address
):
47 self
.ui_dsl_mm
= metamodel_from_file("ui_logger_dsl_grammar.tx")
48 self
.output_stream
= self
.initiate_test_generation(output_address
)
49 self
.input_address
= input_address
51 def get_log_file(self
, input_address
):
54 content
= self
.ui_dsl_mm
.model_from_file(input_address
)
55 except IOError as err
:
56 print("IO error: {0}".format(err
))
58 "Use " + os
.path
.basename(sys
.argv
[0]) + " -h to get usage instructions"
64 def initiate_test_generation(self
, output_address
):
65 self
.last_parent
.append("MainWindow")
67 f
= open(output_address
, "w")
68 except IOError as err
:
69 print("IO error: {0}".format(err
))
71 "Use " + os
.path
.basename(sys
.argv
[0]) + " -h to get usage instructions"
75 "# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-\n\n"
76 + "from uitest.framework import UITestCase\n"
77 + "from libreoffice.uno.propertyvalue import mkPropertyValues\n"
78 + "import importlib\n\n"
79 + "class TestClass(UITestCase):\n"
81 + "def test_function(self):\n"
84 self
.variables
.append(line
)
89 self
.ui_dsl_mm
.register_obj_processors(
91 "UNOCommand": self
.handle_uno
,
92 "StarterCommand": self
.handle_start
,
93 "CloseDialog": self
.handle_Dialog
,
94 "OpenModelessDialog": self
.handle_Dialog
,
95 "OpenModalDialog": self
.handle_Dialog
,
96 "ButtonUIObject": self
.handle_button
,
97 "CheckBoxUIObject": self
.handle_check_box
,
98 "TabControlUIObject": self
.handle_tab
,
99 "ComboBoxUIObject": self
.handle_Combo_box
,
100 "RadioButtonUIObject": self
.handle_Radio_button
,
101 "ListBoxUIObject": self
.handle_List_box
,
102 "SpinFieldUIObject": self
.handle_spin_field
,
103 "EditUIObject": self
.handle_Edit_uiObject
,
104 "writer_Type_command": self
.handle_writer_type
,
105 "writer_Select_command": self
.handle_writer_select
,
106 "writer_GOTO_command": self
.handle_writer_goto
,
107 "calc_Select_cell": self
.handle_calc_select
,
108 "calc_switch_sheet": self
.handle_calc_switch_sheet
,
109 "calc_Type_command": self
.handle_calc_Type_command
,
110 "calc_AutoFill_filter": self
.handle_calc_AutoFill_filter
,
111 "impress_Type_command": self
.handle_impress_Type_command
,
112 "math_element_selector": self
.handle_math_element_selector
,
113 "math_Type_command": self
.handle_math_Type_command
,
114 "setZoom_command": self
.handle_setZoom_command
,
115 "draw_Type_command": self
.handle_draw_Type_command
,
116 "SideBar": self
.handle_SideBar
,
117 "writer_Copy_Text": self
.do_nothing
,
118 "writer_Cut_Text": self
.do_nothing
,
119 "writer_Paste_Text": self
.do_nothing
,
120 "writer_Insert_BreakPage": self
.do_nothing
,
121 "writer_Create_table": self
.do_nothing
,
122 "calc_Remove_Content": self
.do_nothing
,
123 "calc_Delete_Cells": self
.do_nothing
,
124 "calc_insert_cells": self
.do_nothing
,
125 "calc_Cut_Cells": self
.do_nothing
,
126 "calc_Copy_Cells": self
.do_nothing
,
127 "calc_Merge_Cells": self
.do_nothing
,
128 "calc_UNMerge_Cells": self
.do_nothing
,
129 "calc_Rename_Sheet": self
.do_nothing
,
130 "calc_Insert_sheet": self
.do_nothing
,
131 "impress_Insert_Slide": self
.do_nothing
,
132 "impress_Delete_Page": self
.do_nothing
,
133 "impress_Duplicate_Slide": self
.do_nothing
,
134 "impress_Rename_Slide": self
.do_nothing
,
135 "draw_Insert_Page": self
.do_nothing
,
136 "draw_Delete_Page": self
.do_nothing
,
137 "draw_Rename_Page": self
.do_nothing
,
141 self
.log_lines
= self
.get_log_file(self
.input_address
)
144 if self
.current_app
in self
.objects
:
145 self
.objects
[self
.current_app
] += 1
147 self
.objects
[self
.current_app
] = 1
151 + ' = MainWindow.getChild("'
155 self
.variables
.append(line
)
157 def init_Object(self
, Id_of_Object
, name_of_child
, Obj_parent
):
159 if Id_of_Object
in self
.objects
:
160 self
.objects
[Id_of_Object
] += 1
162 self
.objects
[Id_of_Object
] = 1
172 self
.variables
.append(line
)
174 def write_line_without_parameters(self
, Action_holder
, Action
, Action_type
):
184 self
.variables
.append(line
)
186 def write_line_with_one_parameters(
187 self
, Action_holder
, Action
, Paramerter_name
, parameter_value
194 + '", mkPropertyValues({"'
197 + str(parameter_value
)
200 self
.variables
.append(line
)
202 def write_line_with_two_parameters(
217 + '", mkPropertyValues({"'
220 + str(parameter_value_1
)
224 + str(parameter_value_2
)
227 self
.variables
.append(line
)
229 def handle_uno(self
, UNOCommand
):
230 if UNOCommand
.parameters
== None:
233 + 'self.xUITest.executeCommand("'
234 + UNOCommand
.uno_command_name
239 for p
in UNOCommand
.parameters
.parameter_data
:
240 parameters
= parameters
+ '"' + p
.key
+ '" : ' + str(p
.value
) + " ,"
241 parameters
= parameters
[:-1]
245 + 'self.xUITest.executeCommandWithParameters("'
246 + UNOCommand
.uno_command_name
247 + '", mkPropertyValues({'
252 self
.variables
.append(line
)
253 self
.prev_command
= UNOCommand
255 def handle_start(self
, StarterCommand
):
258 + 'MainDoc = self.ui_test.create_doc_in_start_center("'
259 + StarterCommand
.program_name
262 self
.variables
.append(line
)
264 line
= double_tab
+ "MainWindow = self.xUITest.getTopFocusWindow()\n"
265 self
.variables
.append(line
)
267 "writer": "writer_edit",
268 "calc": "grid_window",
269 "impress": "impress_win",
273 self
.current_app
= app
[StarterCommand
.program_name
]
274 self
.prev_command
= StarterCommand
276 def handle_SideBar(self
, SideBar
):
278 line
= ' self.xUITest.executeCommand(".uno:Sidebar")\n'
279 self
.variables
.append(line
)
281 self
.write_line_with_one_parameters(
282 "MainWindow", "SIDEBAR", "PANEL", SideBar
.name
285 self
.prev_command
= SideBar
287 def handle_Dialog(self
, DialogCommand
):
289 if DialogCommand
.__class
__.__name
__ == "OpenModalDialog":
291 if DialogCommand
.dialog_name
!= "QuerySaveDialog":
292 # This part is just to ignore saving the Save dialog while closing the app
294 old_line
= self
.variables
.pop()
295 if self
.prev_command
.__class
__.__name
__ == "UNOCommand":
296 key_word
= self
.prev_command
.uno_command_name
[-6:]
298 key_word
= old_line
[-9:-3]
300 if key_word
== "Dialog":
303 + 'self.ui_test.execute_dialog_through_command("'
304 + self
.prev_command
.uno_command_name
307 self
.variables
.append(old_line
)
310 + DialogCommand
.dialog_name
311 + " = self.xUITest.getTopFocusWindow()\n"
313 self
.variables
.append(line
)
314 self
.last_parent
.append(DialogCommand
.dialog_name
)
315 self
.parent_hierarchy_count
= self
.parent_hierarchy_count
+ 1
318 self
.flag_for_QuerySaveDialog
= True
320 elif DialogCommand
.__class
__.__name
__ == "OpenModelessDialog":
321 old_line
= self
.variables
.pop()
322 if self
.prev_command
.__class
__.__name
__ == "UNOCommand":
323 key_word
= self
.prev_command
.uno_command_name
[-6:]
325 key_word
= old_line
[-9:-3]
327 if key_word
== "Dialog":
330 + 'self.ui_test.execute_modeless_dialog_through_command("'
331 + self
.prev_command
.uno_command_name
334 self
.variables
.append(old_line
)
337 + DialogCommand
.dialog_name
338 + " = self.xUITest.getTopFocusWindow()\n"
340 self
.variables
.append(line
)
341 self
.last_parent
.append(DialogCommand
.dialog_name
)
342 self
.parent_hierarchy_count
= self
.parent_hierarchy_count
+ 1
344 elif DialogCommand
.__class
__.__name
__ == "CloseDialog":
346 if not (self
.flag_for_QuerySaveDialog
):
347 # This part is just to ignore saving the Save dialog while closing the app
349 if self
.prev_command
.__class
__.__name
__ == "ButtonUIObject":
350 old_line
= self
.variables
.pop()
352 if keyword
.iskeyword(self
.prev_command
.ui_button
):
355 + "self.ui_test.close_dialog_through_button(x"
356 + self
.prev_command
.ui_button
362 + "self.ui_test.close_dialog_through_button("
363 + self
.prev_command
.ui_button
366 self
.variables
.append(line
)
367 self
.last_parent
.pop()
368 self
.parent_hierarchy_count
= self
.parent_hierarchy_count
- 1
370 self
.flag_for_QuerySaveDialog
= False
372 self
.prev_command
= DialogCommand
374 def handle_button(self
, ButtonUIObject
):
376 if ButtonUIObject
.parent_id
!= "QuerySaveDialog":
377 # This part is just to ignore saving the Save dialog while closing the app
380 if keyword
.iskeyword(ButtonUIObject
.ui_button
):
381 name_of_obj
= "x" + ButtonUIObject
.ui_button
383 name_of_obj
= ButtonUIObject
.ui_button
385 if ButtonUIObject
.parent_id
== "":
388 ButtonUIObject
.ui_button
,
389 self
.last_parent
[self
.parent_hierarchy_count
],
393 name_of_obj
, ButtonUIObject
.ui_button
, ButtonUIObject
.parent_id
396 self
.write_line_without_parameters(name_of_obj
, "CLICK", "tuple")
398 self
.prev_command
= ButtonUIObject
400 def handle_check_box(self
, CheckBoxUIObject
):
403 if keyword
.iskeyword(CheckBoxUIObject
.Check_box_id
):
404 name_of_obj
= "x" + CheckBoxUIObject
.Check_box_id
406 name_of_obj
= CheckBoxUIObject
.Check_box_id
408 if CheckBoxUIObject
.parent_id
== "":
411 CheckBoxUIObject
.Check_box_id
,
412 self
.last_parent
[self
.parent_hierarchy_count
],
416 name_of_obj
, CheckBoxUIObject
.Check_box_id
, CheckBoxUIObject
.parent_id
419 self
.write_line_without_parameters(name_of_obj
, "CLICK", "tuple")
421 self
.prev_command
= CheckBoxUIObject
423 def handle_tab(self
, TabControlUIObject
):
426 if keyword
.iskeyword(TabControlUIObject
.tab_id
):
427 name_of_obj
= "x" + TabControlUIObject
.tab_id
429 name_of_obj
= TabControlUIObject
.tab_id
431 if TabControlUIObject
.parent_id
== "":
434 TabControlUIObject
.tab_id
,
435 self
.last_parent
[self
.parent_hierarchy_count
],
439 name_of_obj
, TabControlUIObject
.tab_id
, TabControlUIObject
.parent_id
442 self
.write_line_with_one_parameters(
443 name_of_obj
, "SELECT", "POS", TabControlUIObject
.tab_page_number
446 self
.prev_command
= TabControlUIObject
448 def handle_Combo_box(self
, ComboBoxUIObject
):
451 if keyword
.iskeyword(ComboBoxUIObject
.Combo_box_id
):
452 name_of_obj
= "x" + ComboBoxUIObject
.Combo_box_id
454 name_of_obj
= ComboBoxUIObject
.Combo_box_id
456 if ComboBoxUIObject
.parent_id
== "":
459 ComboBoxUIObject
.Combo_box_id
,
460 self
.last_parent
[self
.parent_hierarchy_count
],
464 name_of_obj
, ComboBoxUIObject
.Combo_box_id
, ComboBoxUIObject
.parent_id
467 self
.write_line_with_one_parameters(
468 name_of_obj
, "SELECT", "POS", ComboBoxUIObject
.item_num
471 self
.prev_command
= ComboBoxUIObject
473 def handle_Radio_button(self
, RadioButtonUIObject
):
476 if keyword
.iskeyword(RadioButtonUIObject
.Radio_button_id
):
477 name_of_obj
= "x" + RadioButtonUIObject
.Radio_button_id
479 name_of_obj
= RadioButtonUIObject
.Radio_button_id
481 if RadioButtonUIObject
.parent_id
== "":
484 RadioButtonUIObject
.Radio_button_id
,
485 self
.last_parent
[self
.parent_hierarchy_count
],
490 RadioButtonUIObject
.Radio_button_id
,
491 RadioButtonUIObject
.parent_id
,
494 self
.write_line_without_parameters(name_of_obj
, "CLICK", "tuple")
496 self
.prev_command
= RadioButtonUIObject
498 def handle_List_box(self
, ListBoxUIObject
):
501 if keyword
.iskeyword(ListBoxUIObject
.list_id
):
502 name_of_obj
= "x" + ListBoxUIObject
.list_id
504 name_of_obj
= ListBoxUIObject
.list_id
506 if ListBoxUIObject
.parent_id
== "":
509 ListBoxUIObject
.list_id
,
510 self
.last_parent
[self
.parent_hierarchy_count
],
514 name_of_obj
, ListBoxUIObject
.list_id
, ListBoxUIObject
.parent_id
517 self
.write_line_with_one_parameters(
518 name_of_obj
, "SELECT", "POS", ListBoxUIObject
.POS
521 self
.prev_command
= ListBoxUIObject
523 def handle_spin_field(self
, SpinFieldUIObject
):
526 if keyword
.iskeyword(SpinFieldUIObject
.Spin_id
):
527 name_of_obj
= "x" + SpinFieldUIObject
.Spin_id
529 name_of_obj
= SpinFieldUIObject
.Spin_id
531 if SpinFieldUIObject
.parent_id
== "":
534 SpinFieldUIObject
.Spin_id
,
535 self
.last_parent
[self
.parent_hierarchy_count
],
539 name_of_obj
, SpinFieldUIObject
.Spin_id
, SpinFieldUIObject
.parent_id
542 if SpinFieldUIObject
.change
== "Increase":
543 self
.write_line_without_parameters(name_of_obj
, "UP", "tuple")
544 elif SpinFieldUIObject
.change
== "Decrease":
545 self
.write_line_without_parameters(name_of_obj
, "DOWN", "tuple")
546 self
.prev_command
= SpinFieldUIObject
548 def handle_Edit_uiObject(self
, EditUIObject
):
551 if keyword
.iskeyword(EditUIObject
.action
.edit_button
):
552 name_of_obj
= "x" + EditUIObject
.action
.edit_button
554 name_of_obj
= EditUIObject
.action
.edit_button
556 if EditUIObject
.parent_id
== "":
559 EditUIObject
.action
.edit_button
,
560 self
.last_parent
[self
.parent_hierarchy_count
],
564 name_of_obj
, EditUIObject
.action
.edit_button
, EditUIObject
.parent_id
567 if EditUIObject
.action
.__class
__.__name
__ == "Type_action":
569 if EditUIObject
.action
.what_to_type
.__class
__.__name
__ == "char":
570 self
.write_line_with_one_parameters(
574 EditUIObject
.action
.what_to_type
.input_char
,
577 elif EditUIObject
.action
.what_to_type
.__class
__.__name
__ == "KeyCode":
578 self
.write_line_with_one_parameters(
582 EditUIObject
.action
.what_to_type
.input_key_code
,
585 if EditUIObject
.action
.__class
__.__name
__ == "SELECT":
587 self
.write_line_with_two_parameters(
591 EditUIObject
.action
.from_pos
,
593 EditUIObject
.action
.to_pos
,
596 if EditUIObject
.action
.__class
__.__name
__ == "Clear":
598 self
.write_line_without_parameters(name_of_obj
, "CLEAR", "tuple")
600 self
.prev_command
= EditUIObject
602 def handle_writer_type(self
, writer_Type_command
):
606 if writer_Type_command
.what_to_type
.__class
__.__name
__ == "char":
607 self
.write_line_with_one_parameters(
611 writer_Type_command
.what_to_type
.input_char
,
614 elif writer_Type_command
.what_to_type
.__class
__.__name
__ == "KeyCode":
615 self
.write_line_with_one_parameters(
619 writer_Type_command
.what_to_type
.input_key_code
,
622 self
.prev_command
= writer_Type_command
624 def handle_writer_select(self
, writer_Select_command
):
628 self
.write_line_with_two_parameters(
632 writer_Select_command
.from_pos
,
634 writer_Select_command
.to_pos
,
637 self
.prev_command
= writer_Select_command
639 def handle_writer_goto(self
, writer_GOTO_command
):
643 self
.write_line_with_one_parameters(
644 self
.current_app
, "GOTO", "PAGE", writer_GOTO_command
.page_num
647 self
.prev_command
= writer_GOTO_command
649 def handle_calc_select(self
, calc_Select_cell
):
653 if calc_Select_cell
.select_op
.__class
__.__name
__ == "range_of_cells":
654 self
.write_line_with_one_parameters(
658 calc_Select_cell
.select_op
.input_range
,
661 elif calc_Select_cell
.select_op
.__class
__.__name
__ == "one_cell":
662 self
.write_line_with_one_parameters(
666 calc_Select_cell
.select_op
.input_cell
,
669 self
.prev_command
= calc_Select_cell
671 def handle_calc_switch_sheet(self
, calc_switch_sheet
):
675 self
.write_line_with_one_parameters(
676 self
.current_app
, "SELECT", "TABLE", calc_switch_sheet
.sheet_num
679 self
.prev_command
= calc_switch_sheet
681 def handle_calc_Type_command(self
, calc_Type_command
):
685 if calc_Type_command
.what_to_type
.__class
__.__name
__ == "char":
686 self
.write_line_with_one_parameters(
690 calc_Type_command
.what_to_type
.input_char
,
693 elif calc_Type_command
.what_to_type
.__class
__.__name
__ == "KeyCode":
694 self
.write_line_with_one_parameters(
698 calc_Type_command
.what_to_type
.input_key_code
,
701 self
.prev_command
= calc_Type_command
703 def handle_calc_AutoFill_filter(self
, calc_AutoFill_filter
):
710 + '.executeAction("LAUNCH", mkPropertyValues'
711 + '({"AUTOFILTER": "", "COL": "'
712 + str(calc_AutoFill_filter
.col_num
)
715 + str(calc_AutoFill_filter
.row_num
)
719 self
.variables
.append(line
)
720 self
.prev_command
= calc_AutoFill_filter
722 def handle_impress_Type_command(self
, impress_Type_command
):
726 if impress_Type_command
.what_to_type
.__class
__.__name
__ == "char":
727 self
.write_line_with_one_parameters(
731 impress_Type_command
.what_to_type
.input_char
,
734 elif impress_Type_command
.what_to_type
.__class
__.__name
__ == "KeyCode":
735 self
.write_line_with_one_parameters(
739 impress_Type_command
.what_to_type
.input_key_code
,
742 self
.prev_command
= impress_Type_command
744 def handle_math_Type_command(self
, math_Type_command
):
747 if math_Type_command
.what_to_type
.__class
__.__name
__ == "char":
748 self
.write_line_with_one_parameters(
752 math_Type_command
.what_to_type
.input_char
,
755 elif math_Type_command
.what_to_type
.__class
__.__name
__ == "KeyCode":
756 self
.write_line_with_one_parameters(
760 math_Type_command
.what_to_type
.input_key_code
,
763 self
.prev_command
= math_Type_command
765 def handle_draw_Type_command(self
, draw_Type_command
):
768 if draw_Type_command
.what_to_type
.__class
__.__name
__ == "char":
769 self
.write_line_with_one_parameters(
773 draw_Type_command
.what_to_type
.input_char
,
776 elif draw_Type_command
.what_to_type
.__class
__.__name
__ == "KeyCode":
777 self
.write_line_with_one_parameters(
781 draw_Type_command
.what_to_type
.input_key_code
,
784 self
.prev_command
= draw_Type_command
786 def handle_math_element_selector(self
, math_element_selector
):
790 + str(math_element_selector
.element_no
)
791 + ' = element_selector.getChild("'
792 + str(math_element_selector
.element_no
)
795 self
.variables
.append(line
)
797 self
.write_line_without_parameters(
798 str(math_element_selector
.element_no
), "SELECT", "tuple"
801 self
.prev_command
= math_element_selector
803 def handle_setZoom_command(self
, setZoom_command
):
807 self
.write_line_with_one_parameters(
808 self
.current_app
, "SET", "ZOOM", setZoom_command
.zoom_value
811 self
.prev_command
= setZoom_command
813 def Generate_UI_test(self
):
814 line
= double_tab
+ "self.ui_test.close_doc()"
815 self
.variables
.append(line
)
817 line
= "\n\n# vim: set shiftwidth=4 softtabstop=4 expandtab:"
818 self
.variables
.append(line
)
820 for line
in self
.variables
:
821 self
.output_stream
.write(str(line
))
823 def do_nothing(self
, Command
):
824 line
= "to be added in the future"
827 self
.output_stream
.close()
832 ui_logger
= ul_Compiler(args
.input_address
, args
.output_address
)
834 for statement
in ui_logger
.log_lines
.commands
:
836 ui_logger
.Generate_UI_test()
840 if __name__
== "__main__":