note description: "[ Class to generate queryables into ARFF format Usage: call mutiple times `extend_queryable', then call `generate_maximal_arff', then call `wipe_out' to clean up. ]" author: "" date: "$Date$" revision: "$Revision$" class SEM_ARFF_GENERATOR inherit SEM_QUERYABLE_VISITOR REFACTORING_HELPER SEM_SHARED_EQUALITY_TESTER SEM_FIELD_NAMES ITP_SHARED_CONSTANTS EPA_UTILITY EPA_SHARED_EQUALITY_TESTERS SEM_UTILITY EPA_STRING_UTILITY create make_for_feature_transition, make_for_objects, make_for_snippet feature{NONE} -- Initialization make_for_feature_transition -- Initialize Current for feature calls. do is_for_feature_transition := True initialize ensure is_for_feature_transition_set: is_for_feature_transition end make_for_objects -- Initialize Current for objects. do is_for_objects := True initialize ensure is_for_objects_set: is_for_objects end make_for_snippet -- Initialize Current for snippet. do is_for_snippet := True initialize ensure is_for_snippet_set: is_for_snippet end feature -- Access last_relations: HASH_TABLE [WEKA_ARFF_RELATION, STRING] -- Relations that are generated by last `generate_files' -- Keys are names of those relations, values are the corresponding relations. -- Only populated when `should_populate_last_relations' is True. last_fault_relations: HASH_TABLE [WEKA_ARFF_RELATION, STRING] -- Relations for faults that are genreated by last `generate_files'. -- Keys are names of those relations, values are the corresponding relations. -- Only populated when `should_populate_last_relations' is True and generating for transitions. last_all_relation: detachable WEKA_ARFF_RELATION -- The relation containing all information generated by last `generate_files' -- Only populated when `should_populate_last_relations' is True. last_only_passing_relation: detachable WEKA_ARFF_RELATION -- The relation that contains only information about passing test cases, -- generated by last `generate_files'. -- Only populated when `should_populate_last_relations' is True and generating for transitions. feature -- Status report should_generate_arff_for_all_test_cases: BOOLEAN -- Should an ARFF file be generated for all test cases (passing, -- failing test cases for all faults) -- Default: True should_generate_arff_for_only_passing_test_cases: BOOLEAN -- Should an ARFF file be generated for only passing test cases? -- Default: True should_generate_arff_for_each_fault: BOOLEAN -- Should an ARFF file be generated for each fault (along with passing test cases)? -- Default: True feature -- Setting set_should_generate_arff_for_all_test_cases (b: BOOLEAN) -- Set `should_generate_arff_for_all_test_cases' with `b'. do should_generate_arff_for_all_test_cases := b ensure should_generate_arff_for_all_test_cases_set: should_generate_arff_for_all_test_cases = b end set_should_generate_arff_for_only_passing_test_cases (b: BOOLEAN) -- Set `should_generate_arff_for_only_passing_test_cases' with `b'. do should_generate_arff_for_only_passing_test_cases := b ensure should_generate_arff_for_only_passing_test_cases_set: should_generate_arff_for_only_passing_test_cases = b end set_should_generate_arff_for_each_fault (b: BOOLEAN) -- Set `should_generate_arff_for_each_fault' with `b'. do should_generate_arff_for_each_fault := b ensure should_generate_arff_for_each_fault_set: should_generate_arff_for_each_fault = b end set_extra_expression (a_expressions: DS_HASH_SET [EPA_EXPRESSION]) -- Set `extra_expressions' with `a_expressions'. local l_cursor: DS_HASH_SET_CURSOR [EPA_EXPRESSION] do create extra_expressions.make (a_expressions.count) extra_expressions.set_equality_tester (expression_equality_tester) a_expressions.do_all (agent extra_expressions.force_last) end feature -- Basic operatioins extend_queryable (a_queryable: SEM_QUERYABLE; a_meta: detachable HASH_TABLE [STRING, STRING]) -- Add information from `a_queryable' into Current. -- `a_meta' (is not Void) is a hash-table containing meta information for `a_queryable'. -- Key is data name, value is data value require a_queryable_valid: is_queryable_valid (a_queryable) do meta := a_meta a_queryable.process (Current) end wipe_out -- Wipe out all data that has been processed so far. -- Prepare for new generation. do reset end generate (a_base_relation_name: STRING) -- Generate maximal ARFF from data in Current, storing results -- in `last_relations', `last_fault_relations', `last_only_passing_relation' and `last_fault_relations'. -- In this mode, all expressions in all added queryables will appear -- as an attribute in the final ARFF data. If some expressions only appear in some -- queryables, in the ARFF data, there will be missing values in lines -- for queryables in which those expressions does not appear. -- `a_base_relation_name' is the basic part of the ARFF relation name, depending on the type of -- ARFF file to generate (see `should_generate_arff_for_all_test_cases'), extra part -- will be added into the final file name. do generate_files (a_base_relation_name, Void) end generate_files (a_base_relation_name: STRING; a_directory: detachable STRING) -- Generate maximal ARFF from data in Current. -- In this mode, all expressions in all added queryables will appear -- as an attribute in the final ARFF data. If some expressions only appear in some -- queryables, in the ARFF data, the will be missing values in lines -- for queryables in which those expressions does not appear. -- `a_base_relation_name' is the basic part of the ARFF relation name, depending on the type of -- ARFF file to generate (see `should_generate_arff_for_all_test_cases'), extra part -- will be added into the final file name. -- `a_directory' is the directory to generate ARFF files. If `a_directory' is Void, -- no files will be generated. local l_arff_relation: WEKA_ARFF_RELATION l_attributes: ARRAYED_LIST [WEKA_ARFF_ATTRIBUTE] l_instance: HASH_TABLE [STRING, STRING] l_attr_name: STRING l_attr_value: STRING l_instance_values: ARRAYED_LIST [STRING] do create last_relations.make (5) last_relations.compare_objects create last_fault_relations.make (5) last_fault_relations.compare_objects last_all_relation := Void last_only_passing_relation := Void l_attributes := arff_attributes create l_arff_relation.make (l_attributes) l_arff_relation.set_name (a_base_relation_name) across instances as l_instances loop l_instance := l_instances.item create l_instance_values.make (l_attributes.count) across l_attributes as l_attrs loop l_attr_name := l_attrs.item.name l_attr_value := arff_value (l_attrs.item, l_instance, l_attr_name) l_instance_values.extend (l_attr_value) end l_arff_relation.extend (l_instance_values) end if should_generate_arff_for_all_test_cases then last_relations.force (l_arff_relation, a_base_relation_name + all_arff) last_all_relation := l_arff_relation if a_directory /= Void then generate_arff_file (l_arff_relation, a_base_relation_name + all_arff + ".arff", a_directory) end end if should_generate_arff_for_only_passing_test_cases then generate_maximal_arff_for_only_passing_test_cases (l_arff_relation, a_base_relation_name, a_directory) end if should_generate_arff_for_each_fault then generate_maximal_arff_for_each_fault (l_arff_relation, a_base_relation_name, a_directory) end end generate_arff_file (a_relation: WEKA_ARFF_RELATION; a_file_name: STRING; a_directory: STRING) -- Generate ARFF file from `a_relation' into a file named `a_file_name' in `a_directory'. local l_file_name: FILE_NAME l_file: PLAIN_TEXT_FILE do create l_file_name.make_from_string (a_directory) l_file_name.set_file_name (a_file_name) create l_file.make_create_read_write (l_file_name) a_relation.to_medium (l_file) l_file.close end feature{NONE} -- Implementation generate_maximal_arff_for_only_passing_test_cases (a_relation: WEKA_ARFF_RELATION; a_base_file_name: STRING; a_directory: detachable STRING) -- Generate ARFF for only passing test cases in `a_relation'. -- `a_name' is the name of the ARFF relation to be generated. -- `a_base_file_name' is the basic part of the ARFF file name, depending on the type of -- ARFF file to generate (see `should_generate_arff_for_all_test_cases'), extra part -- will be added into the final file name. -- `a_directory' is the directory to generate ARFF files. local l_only_passing_relation: WEKA_ARFF_RELATION l_fault_attr: WEKA_ARFF_ATTRIBUTE do l_only_passing_relation := a_relation.content_cloned_object l_fault_attr := l_only_passing_relation.attribute_by_name (fault_signature_attribute_name) -- Remove all values representing faults. across l_only_passing_relation.values_of_attribute (l_fault_attr) as l_values loop if l_values.item /~ nonsensical_value then l_only_passing_relation.remove_value (l_fault_attr, l_values.item) end end last_relations.force (l_only_passing_relation, a_base_file_name + only_passing_arff) last_only_passing_relation := l_only_passing_relation if a_directory /= Void then generate_arff_file (l_only_passing_relation, a_base_file_name + only_passing_arff + ".arff", a_directory) end end generate_maximal_arff_for_each_fault (a_relation: WEKA_ARFF_RELATION; a_base_file_name: STRING; a_directory: detachable STRING) -- Generate an ARFF for each fault (including all passing test cases as well). -- `a_name' is the name of the ARFF relation to be generated. -- `a_base_file_name' is the basic part of the ARFF file name, depending on the type of -- ARFF file to generate (see `should_generate_arff_for_all_test_cases'), extra part -- will be added into the final file name. -- `a_directory' is the directory to generate ARFF files. local l_relation: WEKA_ARFF_RELATION l_fault_attr: WEKA_ARFF_ATTRIBUTE l_left_values: LINKED_LIST [STRING] l_ori_values: DS_HASH_SET [STRING] l_removed_values: DS_HASH_SET [STRING] l_cursor: DS_HASH_SET_CURSOR [STRING] do l_fault_attr := a_relation.attribute_by_name (fault_signature_attribute_name) l_ori_values := a_relation.value_set_of_attribute (l_fault_attr) -- Remove all values representing faults. from l_cursor := l_ori_values.new_cursor l_cursor.start until l_cursor.after loop if l_cursor.item /~ nonsensical_value then create l_left_values.make l_left_values.compare_objects l_left_values.extend (nonsensical_value) l_left_values.extend (l_cursor.item) l_relation := a_relation.content_cloned_object l_removed_values := l_ori_values.cloned_object across l_left_values as l_vars loop if l_removed_values.has (l_vars.item) then l_removed_values.remove (l_vars.item) end end if not l_removed_values.is_empty then l_relation.remove_values (l_fault_attr, l_removed_values) end last_relations.force (l_relation, a_base_file_name + fault_arff + l_cursor.item) last_fault_relations.force (l_relation, a_base_file_name + fault_arff + l_cursor.item) if a_directory /= Void then generate_arff_file (l_relation, a_base_file_name + fault_arff + l_cursor.item + ".arff", a_directory) end end l_cursor.forth end end feature -- Status report is_for_feature_transition: BOOLEAN -- Is Current configured for feature transitions? is_for_objects: BOOLEAN -- Is Current configured for objects? is_for_snippet: BOOLEAN -- Is Current configured for snippet? is_queryable_valid (a_queryable: SEM_QUERYABLE): BOOLEAN -- Is `a_queryable' of correct type? do if is_for_feature_transition then Result := a_queryable.is_feature_call elseif is_for_objects then Result := a_queryable.is_objects elseif is_for_snippet then Result := a_queryable.is_snippet end end feature{NONE} -- Process process_snippet (a_snippet: SEM_SNIPPET) -- Process `a_snippet'. do to_implement ("Support snippet later. 30.11.2010 Jasonw") end process_feature_call (a_call: SEM_FEATURE_CALL_TRANSITION) -- Process `a_call'. local l_cursor: DS_HASH_TABLE_CURSOR [LIST [EPA_EXPRESSION_CHANGE], EPA_EXPRESSION] l_change: EPA_EXPRESSION_CHANGE l_expr: EPA_EXPRESSION l_change_kind: INTEGER l_value: EPA_EXPRESSION_VALUE l_extra_expressions: like extra_expressions_for_transitions l_evaluations: like expression_evaluations l_hit_breakpoints: DS_HASH_SET [INTEGER] do last_queryable := a_call extend_instance -- Generate attributes for meta data such as feature name, class name. extend_meta_attribute (class_attribute_name, a_call.class_.name, void_type) extend_meta_attribute (feature_attribute_name, a_call.feature_.feature_name, void_type) if a_call.uuid /= Void then extend_meta_attribute (uuid_attribute_name, a_call.uuid, void_type) end if meta /= Void and then meta.has (fault_id_field) then extend_meta_attribute (fault_signature_attribute_name, meta.item (fault_id_field), none_type) else extend_meta_attribute (fault_signature_attribute_name, nonsensical_value, none_type) end if meta /= Void and then meta.has (exception_tag_field) then extend_meta_attribute (exception_tag_attribute_name, meta.item (exception_tag_field), none_type) else extend_meta_attribute (exception_tag_attribute_name, nonsensical_value, none_type) end extend_operand_equality_attributes (a_call) -- Generate attributes for pre- and post-conditions. a_call.interface_preconditions.do_if (agent extend_attribute (?, property_kind_precondition, a_call), agent is_equation_suitable_as_attribute (?, a_call)) a_call.interface_postconditions.do_if (agent extend_attribute (?, property_kind_postcondition, a_call), agent is_equation_suitable_as_attribute (?, a_call)) -- Generate attributes for extra expressions including interesting expression, path conditions -- extracted from the source code of the feature under analysis. create_expresion_evaluator (a_call) l_extra_expressions := extra_expressions_for_transitions (a_call, True) l_evaluations := expression_evaluations (l_extra_expressions) l_evaluations.do_all (agent extend_attribute (?, property_kind_precondition, a_call)) -- Generate attributes for changes. from l_cursor := a_call.changes.new_cursor l_cursor.start until l_cursor.after loop l_expr := l_cursor.key if a_call.is_interface_expression (l_expr) then if l_expr.type.is_integer or l_expr.type.is_boolean then integer_argumented_query_matcher.match (l_expr.text) if not integer_argumented_query_matcher.has_matched then across l_cursor.item as l_changes loop l_change := l_changes.item if l_change.is_absolute then l_change_kind := property_kind_absolute_change else l_change_kind := property_kind_relative_change end if not l_change.values.is_empty then l_value := value_from_expression (l_change.values.first) extend_attribute (create {EPA_EQUATION}.make (l_expr, l_value), l_change_kind, a_call) end end end end end l_cursor.forth end -- Generate attributes for hit breakpoints. l_hit_breakpoints := a_call.hit_breakpoints across 1 |..| a_call.feature_.number_of_breakpoint_slots as l_bps loop generate_hit_breakpoint_attribute (l_bps.item, l_hit_breakpoints.has (l_bps.item), a_call) end end process_objects (a_objects: SEM_OBJECTS) -- Process `a_objects'. do to_implement ("Support objects later. 30.11.2010 Jasonw") end feature{NONE} -- Implementation value_from_expression (a_expression: EPA_EXPRESSION): EPA_EXPRESSION_VALUE -- Value constant from `a_expression' do if a_expression.is_boolean_constant then create {EPA_BOOLEAN_VALUE} Result.make (a_expression.text.to_boolean) elseif a_expression.is_integer_constant then create {EPA_INTEGER_VALUE} Result.make (a_expression.text.to_integer) end end is_equation_suitable_as_attribute (a_equation: EPA_EQUATION; a_transition: SEM_FEATURE_CALL_TRANSITION): BOOLEAN -- Is `a_equation' of boolean or integer type? local l_feat: FEATURE_I l_position: INTEGER l_type: TYPE_A do -- if a_equation.expression.type.is_boolean or a_equation.expression.type.is_integer then if a_transition.inputs.has (a_equation.expression) or else a_transition.outputs.has (a_equation.expression) then l_position := a_transition.operand_variable_positions.item (a_equation.expression) l_feat := a_transition.feature_ if l_position = 0 then Result := True elseif l_position > l_feat.argument_count then if l_feat.has_return_value then Result := True -- Result := l_feat.type.is_integer or l_feat.type.is_boolean end else l_type := l_feat.arguments.i_th (l_position) Result := True -- Result := l_type.is_integer or l_type.is_boolean end else integer_argumented_query_matcher.match (a_equation.text) Result := not integer_argumented_query_matcher.has_matched end -- end end extend_operand_equality_attributes (a_transition: SEM_FEATURE_CALL_TRANSITION) -- Extend attributes to represent equality relation between operands of `a_transition'. local l_operands: EPA_HASH_SET [EPA_EXPRESSION] l_positions: HASH_TABLE [EPA_EXPRESSION, INTEGER] l_pre_positions: HASH_TABLE [EPA_EXPRESSION, INTEGER] l_post_positions: HASH_TABLE [EPA_EXPRESSION, INTEGER] l_cursor: DS_HASH_SET_CURSOR [EPA_EXPRESSION] l_opd_count: INTEGER do -- Collect all operands for `a_transition'. create l_operands.make (10) l_operands.set_equality_tester (expression_equality_tester) a_transition.inputs.union (a_transition.outputs).do_all (agent l_operands.force_last) l_opd_count := operand_count_of_feature (a_transition.feature_) create l_positions.make (l_operands.count) from l_operands.start until l_operands.after loop l_positions.force (l_operands.item_for_iteration, a_transition.variable_positions.item (l_operands.item_for_iteration)) l_operands.forth end -- Generate attributes in pre-state. l_pre_positions := l_positions.twin if a_transition.is_creation then l_pre_positions.remove (0) end if a_transition.is_query then l_pre_positions.remove (l_opd_count - 1) end create l_operands.make (l_pre_positions.count) l_operands.set_equality_tester (expression_equality_tester) across l_pre_positions as l_poses loop l_operands.force_last (l_poses.item) end generate_variable_equality_comparison_attributes (l_operands, a_transition, True) -- Generate attributes in post-state. l_post_positions := l_positions.twin create l_operands.make (l_post_positions.count) l_operands.set_equality_tester (expression_equality_tester) across l_post_positions as l_poses loop l_operands.force_last (l_poses.item) end generate_variable_equality_comparison_attributes (l_operands, a_transition, False) end generate_variable_equality_comparison_attributes (a_operands: DS_HASH_SET [EPA_EXPRESSION]; a_transition: SEM_FEATURE_CALL_TRANSITION; a_precondition: BOOLEAN) -- Generate attributes for "var1 = var2" and "var1 ~ var2" for each variable pair in `a_operands. -- `a_precondition' indicates if those variables are from pre-state. local l_state: EPA_STATE l_cursor: DS_HASH_SET_CURSOR [EPA_EXPRESSION] l_var: EPA_EXPRESSION l_equation: EPA_EQUATION l_expr_text: STRING l_set: EPA_HASH_SET [EPA_EXPRESSION] l_var1: EPA_EXPRESSION l_var2: EPA_EXPRESSION l_expr: EPA_AST_EXPRESSION l_expr1: EPA_EXPRESSION l_expr2: EPA_EXPRESSION do if a_precondition then l_state := a_transition.preconditions else l_state := a_transition.postconditions end create l_set.make (a_operands.count) l_set.set_equality_tester (expression_equality_tester) l_set.append (a_operands) across l_set.combinations (2) as l_pairs loop l_expr1 := l_pairs.item.first l_expr2 := l_pairs.item.last if attached {EPA_BOOLEAN_VALUE} value_of_var1_equal_to_var2 (l_expr1, l_expr2, l_state) as l_bool_value then create l_expr_text.make (64) l_expr_text.append_character ('(') l_expr_text.append (l_expr1.text) l_expr_text.append (once " = ") l_expr_text.append (l_expr2.text) l_expr_text.append_character (')') create l_expr.make_with_text (l_expr1.class_, l_expr1.feature_, l_expr_text, l_expr1.written_class) create l_equation.make (l_expr, l_bool_value) extend_attribute (l_equation, property_kind_precondition, a_transition) end if attached {EPA_BOOLEAN_VALUE} value_of_var1_object_equal_to_var2 (l_expr1, l_expr2, l_state) as l_bool_value then create l_expr_text.make (64) l_expr_text.append_character ('(') l_expr_text.append (l_expr1.text) l_expr_text.append (once " ~ ") l_expr_text.append (l_expr2.text) l_expr_text.append_character (')') create l_expr.make_with_text (l_expr1.class_, l_expr1.feature_, l_expr_text, l_expr1.written_class) create l_equation.make (l_expr, l_bool_value) extend_attribute (l_equation, property_kind_precondition, a_transition) end end end value_of_var1_equal_to_var2 (a_var1: EPA_EXPRESSION; a_var2: EPA_EXPRESSION; a_state: EPA_STATE): detachable EPA_BOOLEAN_VALUE -- The value of the expression `a_var1 = a_var2' evaluated in `a_state' -- Return Void if the value is not calculatable. local l_equ1, l_equ2: EPA_EQUATION l_value1, l_value2: EPA_EXPRESSION_VALUE do l_equ1 := a_state.item_with_expression (a_var1) l_equ2 := a_state.item_with_expression (a_var2) if l_equ1 /= Void and then l_equ2 /= Void then l_value1 := l_equ1.value l_value2 := l_equ2.value if l_value1.is_integer and then l_value2.is_integer then if l_value1.as_integer.item = l_value2.as_integer.item then Result := true_expression_value else Result := false_expression_value end elseif l_value1.is_boolean and then l_value2.is_boolean then if l_value1.as_boolean.item = l_value2.as_boolean.item then Result := true_expression_value else Result := false_expression_value end elseif l_value1.is_reference and then l_value2.is_reference then if l_value1.as_reference.item ~ l_value2.as_reference.item then Result := true_expression_value else Result := false_expression_value end end end end value_of_var1_object_equal_to_var2 (a_var1: EPA_EXPRESSION; a_var2: EPA_EXPRESSION; a_state: EPA_STATE): detachable EPA_BOOLEAN_VALUE -- The value of the expression `a_var1 ~ a_var2' evaluated in `a_state' -- Return Void if the value is not calculatable. local l_equ1, l_equ2: EPA_EQUATION l_value1, l_value2: EPA_EXPRESSION_VALUE do l_equ1 := a_state.item_with_expression (a_var1) l_equ2 := a_state.item_with_expression (a_var2) if l_equ1 /= Void and then l_equ2 /= Void then l_value1 := l_equ1.value l_value2 := l_equ2.value if l_value1.is_integer and then l_value2.is_integer then if l_value1.as_integer.item = l_value2.as_integer.item then Result := true_expression_value else Result := false_expression_value end elseif l_value1.is_boolean and then l_value2.is_boolean then if l_value1.as_boolean.item = l_value2.as_boolean.item then Result := true_expression_value else Result := false_expression_value end elseif l_value1.is_reference and then l_value2.is_reference then if l_value1.as_reference.object_equivalent_class_id = l_value2.as_reference.object_equivalent_class_id then Result := true_expression_value else Result := false_expression_value end end end end true_expression_value: EPA_BOOLEAN_VALUE -- True expression value do create Result.make (True) end false_expression_value: EPA_BOOLEAN_VALUE -- True expression value do create Result.make (False) end class_attribute_name: STRING = "class" feature_attribute_name: STRING = "feature" uuid_attribute_name: STRING = "uuid" fault_signature_attribute_name: STRING = "fault_signature" exception_tag_attribute_name: STRING = "exception_tag" test_case_attribute_name: STRING = "test_case_name" feature{NONE} -- Implementation reset -- Reset data structure for new generation. do create attributes.make (256) attributes.set_equality_tester (string_equality_tester) create attribute_types.make (256) attribute_types.compare_objects create default_values.make (256) default_values.compare_objects create value_strings.make (256) value_strings.set_equality_tester (string_equality_tester) create values_for_attribute.make (256) values_for_attribute.compare_objects create instances.make create integer_argumented_query_matcher.make integer_argumented_query_matcher.compile (".*\([0-9\-]+\).*") end feature{NONE} -- Implementation attributes: DS_HASH_SET [STRING] -- Set of attributes that are corrected so far attribute_types: HASH_TABLE [TYPE_A, STRING] -- Table from attribute names to their types -- keys are name of attributes, values are types of those attributes. instances: LINKED_LIST [HASH_TABLE [STRING, STRING]] -- List of instances, each for an added queryable -- Keys of the hash-table is are attribute names, -- values are the value of those attributes. default_values: HASH_TABLE [STRING, STRING] -- Default values for attributes that are missing in some instances -- Keys are attribute names, values are the default values for those -- attributes. last_instance: detachable HASH_TABLE [STRING, STRING] -- The last extended instance by `extend_instance' value_strings: DS_HASH_SET [STRING] -- Value strings that are seen so far values_for_attribute: HASH_TABLE [DS_HASH_SET [STRING], STRING] -- Values for attributes -- Keys of the hash-table is attribute names -- Values of the hash-table is the set of values for that attribute. meta: detachable HASH_TABLE [STRING, STRING] -- Meta data for the queryable -- Key is data name, value is data value integer_argumented_query_matcher: RX_PCRE_REGULAR_EXPRESSION -- Matcher for integer-argumented queries last_queryable: SEM_QUERYABLE -- Last queryable generate_hit_breakpoint_attribute (a_bp_slot: INTEGER; a_hit: BOOLEAN; a_transition: SEM_FEATURE_CALL_TRANSITION) -- Generate attribute to indicate if breakpoint `a_bp_slot' is hit -- during the test case execution. -- `a_hit' indicates if that breakpoint is hit or not. local l_expr: EPA_AST_EXPRESSION l_text: STRING l_bool: EPA_BOOLEAN_VALUE l_equation: EPA_EQUATION do -- Contruct equation representing the breakpoint hit information. create l_text.make (32) l_text.append (once "hit_breakpoint (") l_text.append_integer (a_bp_slot) l_text.append_character (')') create l_expr.make_with_text_and_type (a_transition.class_, a_transition.feature_, l_text, a_transition.class_, boolean_type) if a_hit then l_bool := true_expression_value else l_bool := false_expression_value end create l_equation.make (l_expr, l_bool) -- Extend new attribute. extend_attribute (l_equation, property_kind_postcondition, a_transition) end feature{NONE} -- Status report has_any_instance: BOOLEAN -- Does `instances' have any instance yet? do Result := instances.count > 1 ensure good_result: Result = (instances.count > 1) end feature{NONE} -- Implementation extend_instance -- Extend a new instance, make the extended instance available -- at `last_instance'. do create last_instance.make (50) last_instance.compare_objects instances.extend (last_instance) end extend_attribute (a_equation: EPA_EQUATION; a_attribute_kind: INTEGER; a_queryable: SEM_QUERYABLE) -- Extend attribute described in `a_equation' into Current generator. -- `a_attribute_kind' indicates the kind of `a_equation'. -- `a_queryable' is the queryable where `a_equation' is from. require a_attribute_kind_valid: is_property_kind_valid (a_attribute_kind) local l_attr_name: STRING l_attributes: like attributes l_type: detachable TYPE_A l_pos_tbl: DS_HASH_TABLE [INTEGER, EPA_EXPRESSION] l_operand_index: INTEGER do -- Construct attribute name. create l_attr_name.make (64) if attached {SEM_FEATURE_CALL_TRANSITION} a_queryable as l_transition then l_pos_tbl := l_transition.variable_positions l_pos_tbl.search (a_equation.expression) if l_pos_tbl.found then l_operand_index := l_pos_tbl.found_item l_type := operand_types_with_feature (l_transition.feature_, l_transition.class_).item (l_operand_index) end end if l_type = Void then l_type := a_equation.expression.type end l_attr_name.append (attribute_prefix (l_type, a_attribute_kind)) l_attr_name.append (anonymous_form (a_equation.expression)) if not attributes.has (l_attr_name) then extend_new_attribute (l_attr_name, a_equation, a_attribute_kind, l_type) end last_instance.force (value_of_attribute (a_equation), l_attr_name) end extra_expressions_for_transitions (a_call: SEM_FEATURE_CALL_TRANSITION; a_precondition: BOOLEAN): DS_HASH_SET [EPA_EXPRESSION] -- Extra expressions for `a_call'. Extra expressions are used to support advanced anslysis. -- `a_precondition' indicates whether those extra expressions should be evaluated in prestate. local l_expr: EPA_EXPRESSION l_text: STRING l_cursor: DS_HASH_SET_CURSOR [EPA_EXPRESSION] l_path: EPA_EXPRESSION l_path_text: STRING l_class: CLASS_C l_feature: FEATURE_I l_feat_id: STRING l_paths: LINKED_LIST [STRING] do create Result.make (10) Result.set_equality_tester (expression_equality_tester) if extra_expressions /= Void then from l_cursor := extra_expressions.new_cursor l_cursor.start until l_cursor.after loop l_expr := expression_in_test_context (a_call, l_cursor.item) if l_expr /= Void and then l_expr.type /= Void then Result.force_last (l_expr) end l_cursor.forth end end -- NOTE: The following adds unvisited path conditions of classes as attributes -- in ARFF files, remove the hard-coded path conditions when the path condition -- calculator finishes. fixme ("Remove this hack. 22.03.2011 Jasonw") l_class := a_call.class_ l_feature := a_call.feature_ l_feat_id := class_name_dot_feature_name (l_class, l_feature) create l_paths.make if l_feat_id ~ "ARRAYED_SET.symdif" then l_paths.extend ("not other.is_empty and Current.is_empty") elseif l_feat_id ~ "BOUNDED_QUEUE.extend" then l_paths.extend ("Current.count >= Current.capacity") elseif l_feat_id ~ "BOUNDED_QUEUE.force" then l_paths.extend ("Current.count >= Current.capacity") elseif l_feat_id ~ "BOUNDED_QUEUE.put" then l_paths.extend ("Current.count >= Current.capacity") elseif l_feat_id ~ "DS_HASH_SET.extend" then l_paths.extend ("other /= Current") elseif l_feat_id ~ "DS_HASH_SET.extend_last" then l_paths.extend ("other /= Current") elseif l_feat_id ~ "DS_HASH_SET.intersect" then l_paths.extend ("other /= Current and other.is_empty") l_paths.extend ("other /= Current and not other.is_empty") elseif l_feat_id ~ "DS_HASH_SET.merge" then l_paths.extend ("other /= Current") elseif l_feat_id ~ "DS_HASH_SET.subtract" then l_paths.extend ("not other.is_empty and other /= Current") elseif l_feat_id ~ "DS_HASH_SET.symdif" then l_paths.extend ("not other.is_empty and other /= Current") l_paths.extend ("not other.is_empty and other /= Current and not Current.is_empty") elseif l_feat_id ~ "DS_LINKED_LIST.append_right_cursor" then l_paths.extend ("not other.is_empty and not Current.is_empty and not a_cursor.before and a_cursor.is_last") elseif l_feat_id ~ "DS_LINKED_LIST.prune_left_cursor" then l_paths.extend ("not a_cursor.after and (n /= 0)") elseif l_feat_id ~ "DS_LINKED_LIST.prune_right_cursor" then l_paths.extend ("not a_cursor.before and (n /= 0)") elseif l_feat_id ~ "DS_LINKED_LIST.put_right_cursor" then l_paths.extend ("not a_cursor.before and not a_cursor.is_last") elseif l_feat_id ~ "DS_LINKED_QUEUE.append" then l_paths.extend ("not other.is_empty") elseif l_feat_id ~ "DS_LINKED_QUEUE.extend" then l_paths.extend ("not other.is_empty") elseif l_feat_id ~ "LINKED_SET.symdif" then l_paths.extend ("not other.is_empty and Current.is_empty") elseif l_feat_id ~ "TWO_WAY_SORTED_SET.merge_left" then l_paths.extend ("not other.is_empty and Current.is_empty and Current.before") elseif l_feat_id ~ "TWO_WAY_SORTED_SET.symdif" then l_paths.extend ("not other.is_empty and Current.is_empty") end across l_paths as l_path_conditions loop create {EPA_AST_EXPRESSION} l_path.make_with_text (l_class, l_feature, l_path_conditions.item, l_class) if l_path.type /= Void then l_path := expression_in_test_context (a_call, l_path) if l_path.type /= Void then Result.force_last (l_path) end end end end expression_evaluations (a_expressions: DS_HASH_SET [EPA_EXPRESSION]): DS_HASH_SET [EPA_EQUATION] -- Evaluations for `a_expressions' using `expression_evaluator' local l_evaluator: like expression_evaluator l_cursor: DS_HASH_SET_CURSOR [EPA_EXPRESSION] l_value: EPA_EXPRESSION_VALUE l_equation: EPA_EQUATION do create Result.make (a_expressions.count) Result.set_equality_tester (equation_equality_tester) l_evaluator := expression_evaluator from l_cursor := a_expressions.new_cursor l_cursor.start until l_cursor.after loop l_evaluator.evaluate (l_cursor.item.ast) if not l_evaluator.has_error then l_value := l_evaluator.last_value if l_value.is_boolean or l_value.is_integer then create l_equation.make (l_cursor.item, l_value) Result.force_last (l_equation) end end l_evaluator.wipe_out_error l_cursor.forth end end create_expresion_evaluator (a_call: SEM_FEATURE_CALL_TRANSITION) -- Create `expression_evaluator' for `a_call'. do create expression_evaluator -- We use preconditions as postconditions. expression_evaluator.set_context (a_call.preconditions, a_call.preconditions, a_call.class_) end value_of_attribute (a_equation: EPA_EQUATION): STRING -- Value for attribute with content `a_equation' require a_equation_valid: a_equation.type.is_boolean or a_equation.type.is_integer do if a_equation.value.is_nonsensical then Result := missing_value_string elseif a_equation.type.is_boolean then if a_equation.value.as_boolean.item then Result := true_string else Result := false_string end else Result := a_equation.value.text end value_strings.search (Result) if value_strings.found then Result := value_strings.found_item else value_strings.force_last (Result) end end extend_new_attribute (a_name: STRING; a_equation: EPA_EQUATION; a_kind: INTEGER; a_type: TYPE_A) -- Extend new attribute name `a_name' with content `a_equation' of property kind `a_kind'. -- `a_type' is the type of the expression in `a_equation'. require a_kind_valid: is_property_kind_valid (a_kind) a_name_not_exist: not attributes.has (a_name) do attributes.force_last (a_name) attribute_types.force (a_type, a_name) default_values.force (default_value (a_type, a_kind), a_name) end extend_meta_attribute (a_name: STRING; a_value: STRING; a_type: TYPE_A) -- Extend a meta attribute named `a_name' with `a_value' local l_values: DS_HASH_SET [STRING] do if not attributes.has (a_name) then attributes.force_last (a_name) attribute_types.force (a_type, a_name) default_values.force (nonsensical_value, a_name) create l_values.make (5) l_values.set_equality_tester (string_equality_tester) values_for_attribute.force (l_values, a_name) end last_instance.force (a_value, a_name) values_for_attribute.item (a_name).force_last (a_value) end zero_string: STRING = "0" true_string: STRING = "1" missing_boolean_string: STRING = "2" false_string: STRING = "0" missing_value_string: STRING = "?" default_value (a_type: TYPE_A; a_attribute_kind: INTEGER): STRING -- Default value for attribute of type `a_type' of property kind `a_attribute_kind' require a_attribute_kind_valid: is_property_kind_valid (a_attribute_kind) a_type_valid: a_type.is_integer or a_type.is_boolean do if is_property_kind_change (a_attribute_kind) then if a_type.is_boolean then Result := missing_boolean_string elseif a_type.is_integer then Result := zero_string end else Result := missing_value_string end end attribute_prefix (a_type: TYPE_A; a_property_kind: INTEGER): STRING -- Prefix for an attribute'. -- `a_type' indicates the type of the attribute. -- `a_property_kind' indicates the kind of the property require a_type_valid: a_type.is_boolean and a_type.is_integer a_property_type_valid: is_property_kind_valid (a_property_kind) do create Result.make (10) Result.append (property_kind_prefix (a_property_kind)) Result.append (attribute_type_prifix (a_type)) end attribute_type_prifix (a_type: TYPE_A): STRING -- Type prefix for `a_type' require a_type_valid: a_type.is_boolean and a_type.is_integer do create Result.make (3) if a_type.is_boolean then Result.append (boolean_type_prefix) elseif a_type.is_integer then Result.append (integer_type_prefix) else Result.append (reference_type_prefix) end end is_property_kind_change (a_property_kind: INTEGER): BOOLEAN -- Is `a_property_kind' change-related? -- That is: either absolute change or relative change. require a_property_kind_valid: is_property_kind_valid (a_property_kind) do Result := a_property_kind = property_kind_absolute_change or else a_property_kind = property_kind_relative_change end arff_attributes: ARRAYED_LIST [WEKA_ARFF_ATTRIBUTE] -- List of ARFF attributes from `attributes' local l_cursor: DS_HASH_SET_CURSOR [STRING] l_type: TYPE_A l_attr_name: STRING l_attribute: WEKA_ARFF_ATTRIBUTE do create Result.make (attributes.count) from l_cursor := attributes.new_cursor l_cursor.start until l_cursor.after loop l_attr_name := l_cursor.item l_type := attribute_types.item (l_attr_name) if l_type.is_boolean or l_type.is_integer then create {WEKA_ARFF_NUMERIC_ATTRIBUTE} l_attribute.make (l_attr_name) -- elseif l_type.is_void then -- create {WEKA_ARFF_STRING_ATTRIBUTE} l_attribute.make (l_attr_name) elseif l_type.is_none then create {WEKA_ARFF_NOMINAL_ATTRIBUTE} l_attribute.make (l_attr_name, values_for_attribute.item (l_attr_name)) else create {WEKA_ARFF_STRING_ATTRIBUTE} l_attribute.make (l_attr_name) end Result.extend (l_attribute) l_cursor.forth end end arff_value (a_attribute: WEKA_ARFF_ATTRIBUTE; a_values: HASH_TABLE [STRING, STRING]; a_attr_name: STRING): STRING -- ARFF value for attribute named `a_attr_name' do a_values.search (a_attr_name) if a_values.found then Result := a_attribute.value (a_values.found_item) else Result := a_attribute.value (default_values.item (a_attr_name)) end end anonymous_form (a_expression: EPA_EXPRESSION): STRING -- Anonymous form of `a_expression' in the context of `last_queryable' do if attached {SEM_FEATURE_CALL_TRANSITION} last_queryable as l_feat then Result := l_feat.anonymous_expression_text (a_expression) else Result := a_expression.text end end initialize -- Initialize Current. do reset set_should_generate_arff_for_all_test_cases (True) set_should_generate_arff_for_each_fault (True) set_should_generate_arff_for_only_passing_test_cases (True) end all_arff: STRING = "__all__noname" only_passing_arff: STRING = "__passing__noname" fault_arff: STRING = "__fault__" expression_in_test_context (a_call: SEM_FEATURE_CALL_TRANSITION; a_expression: EPA_EXPRESSION): EPA_EXPRESSION -- Expression in test context -- For example, `a_expression' = "Current.is_empty", and if in current -- test context Current = v_5, then the result is: v_5.is_empty. local l_replacements: HASH_TABLE [STRING, STRING] l_text: STRING do l_replacements := feature_operand_to_variable_mapping (a_call.feature_, a_call.operand_map) l_text := expression_rewriter.ast_text (a_expression.ast, l_replacements) create {EPA_AST_EXPRESSION} Result.make_with_text (a_call.context.class_, a_call.context.feature_, l_text, a_call.context.class_) end expression_evaluator: EPA_EXPRESSION_EVALUATOR -- Expression evaluator extra_expressions: DS_HASH_SET [EPA_EXPRESSION] -- Extra expressions to be evaluated (if possible) and added -- into the generated ARFF files as attributes end