Changeset 2042 for trunk/autoquest-core-usability/src/main/java/de/ugoe
- Timestamp:
- 10/20/15 10:11:04 (9 years ago)
- Location:
- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/CheckBoxMultipleSelectionRule.java
r1918 r2042 15 15 package de.ugoe.cs.autoquest.usability; 16 16 17 import java.util.ArrayList; 17 18 import java.util.Collection; 18 19 import java.util.HashMap; 19 20 import java.util.HashSet; 21 import java.util.LinkedList; 20 22 import java.util.List; 21 23 import java.util.Map; 22 24 import java.util.Set; 23 25 26 import de.ugoe.cs.autoquest.eventcore.IEventTarget; 27 import de.ugoe.cs.autoquest.eventcore.gui.MouseClick; 28 import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection; 24 29 import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox; 25 30 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 26 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor; 31 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView; 32 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; 27 33 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; 28 34 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; 29 import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;30 import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;31 35 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask; 32 36 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; 33 37 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; 34 import de.ugoe.cs.autoquest.tasktrees.treeifc. TaskMetric;38 import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; 35 39 36 40 /** … … 68 72 UsabilityEvaluationResult results) 69 73 { 74 System.out.println("\n\n########################################\n"); 75 final List<List<IEventTaskInstance>> actionInstancesInSameView = new LinkedList<>(); 76 77 for (IUserSession session : taskModel.getUserSessions()) { 78 for (ITaskInstance instance : session) { 79 final LinkedList<IEventTaskInstance> currentList = new LinkedList<>(); 80 81 instance.accept(new DefaultTaskInstanceTraversingVisitor() { 82 @Override 83 public void visit(IEventTaskInstance eventTaskInstance) { 84 if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) { 85 IEventTarget target = eventTaskInstance.getEvent().getTarget(); 86 IGUIView currentView = ((IGUIElement) target).getView(); 87 88 IGUIView previousView = null; 89 if (currentList.size() > 0) { 90 target = currentList.getLast().getEvent().getTarget(); 91 previousView = ((IGUIElement) target).getView(); 92 } 93 94 if ((previousView == currentView) || 95 ((previousView != null) && (previousView.equals(currentView)))) 96 { 97 currentList.add(eventTaskInstance); 98 } 99 else { 100 if (currentList.size() > 0) { 101 actionInstancesInSameView.add(new ArrayList<>(currentList)); 102 } 103 currentList.clear(); 104 currentList.add(eventTaskInstance); 105 } 106 } 107 } 108 }); 109 110 if (currentList.size() > 0) { 111 actionInstancesInSameView.add(new ArrayList<>(currentList)); 112 } 113 } 114 } 115 116 70 117 Map<IGUIElement, List<IGUIElement>> checkBoxGroups = statistics.getCheckBoxGroups(); 71 118 72 CHECK_NEXT_GROUP: 73 for (List<IGUIElement> group : checkBoxGroups.values()) { 74 Set<ITask> tasksUsingGroup = new HashSet<>(); 75 76 for (IGUIElement checkBox : group) { 77 Set<ITask> tasksUsingCheckBox = getTasksUsingCheckBox(checkBox, taskModel); 119 for (Map.Entry<IGUIElement, List<IGUIElement>> group : checkBoxGroups.entrySet()) { 120 IGUIView currentView = group.getKey().getView(); 121 int noOfEvents = 0; 122 int noOfGroupUsages = 0; 123 int noOfSingleCheckBoxUsages = 0; 124 125 for (List<IEventTaskInstance> actionInstanceList : actionInstancesInSameView) { 126 IEventTarget target = actionInstanceList.get(0).getEvent().getTarget(); 127 IGUIView viewOfList = ((IGUIElement) target).getView(); 78 128 79 for (ITask taskUsingCheckBox : tasksUsingCheckBox) { 80 if (tasksUsingGroup.contains(taskUsingCheckBox)) { 81 continue CHECK_NEXT_GROUP; 82 } 83 else { 84 tasksUsingGroup.add(taskUsingCheckBox); 129 if ((viewOfList == currentView) || 130 ((viewOfList != null) && (viewOfList.equals(currentView)))) 131 { 132 noOfGroupUsages++; 133 boolean[] checkBoxUsages = new boolean[group.getValue().size()]; 134 135 for (IEventTaskInstance actionInstance : actionInstanceList) { 136 int index = 0; 137 for (IGUIElement checkBox : group.getValue()) { 138 if ((("JFC".equals(actionInstance.getEvent().getTarget().getPlatform())) && 139 (actionInstance.getEvent().getType() instanceof MouseClick) && 140 (actionInstance.getEvent().getTarget().equals(checkBox))) || 141 ((actionInstance.getEvent().getType() instanceof ValueSelection<?>) && 142 (actionInstance.getEvent().getTarget().equals(checkBox)))) 143 { 144 checkBoxUsages[index] = !checkBoxUsages[index]; 145 noOfEvents++; 146 } 147 index++; 148 } 149 } 150 151 int noOfCheckedBoxes = 0; 152 153 for (int i = 0; i < checkBoxUsages.length; i++) { 154 if (checkBoxUsages[i]) { 155 noOfCheckedBoxes++; 156 } 157 } 158 159 if (noOfCheckedBoxes == 1) { 160 noOfSingleCheckBoxUsages++; 85 161 } 86 162 } 87 163 } 88 164 89 if (tasksUsingGroup.size() > 0) { 90 int eventCoverage = 0; 91 int allRecordedEvents = 0; 92 93 for (ITask task : tasksUsingGroup) { 94 if (task instanceof IEventTask) { 95 eventCoverage += 96 taskModel.getTaskInfo(task).getMeasureValue(TaskMetric.EVENT_COVERAGE); 97 } 98 } 99 100 for (ITask task : taskModel.getTasks()) { 101 if (task instanceof IEventTask) { 102 allRecordedEvents += 103 taskModel.getTaskInfo(task).getMeasureValue(TaskMetric.EVENT_COVERAGE); 104 } 105 } 106 107 108 UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity 109 ((int) (1000 * eventCoverage / allRecordedEvents), eventCoverage, -1); 110 111 if (intensity != null) { 112 Map<String, Object> parameters = new HashMap<String, Object>(); 113 parameters.put("radioButtons", group); 114 115 results.addSmell 116 (intensity, UsabilitySmellDescription.CHECK_BOX_SINGLE_SELECTION, 117 parameters); 118 } 119 } 120 } 121 } 122 123 /** 124 * 125 */ 126 private Set<ITask> getTasksUsingCheckBox(final IGUIElement checkBox, ITaskModel taskModel) { 127 final Set<ITask> tasksUsingCheckBox = new HashSet<ITask>(); 128 129 for (ITask candidate : taskModel.getTasks()) { 130 candidate.accept(new DefaultTaskTraversingVisitor() { 131 @Override 132 public void visit(IEventTask eventTask) { 133 if (!eventTask.getInstances().isEmpty()) { 134 IEventTaskInstance instance = 135 (IEventTaskInstance) eventTask.getInstances().iterator().next(); 136 137 if (checkBox.equals(instance.getEvent().getTarget())) { 138 tasksUsingCheckBox.add(eventTask); 139 } 140 } 141 } 142 143 @Override 144 public void visit(IStructuringTemporalRelationship relationship) { 145 if (tasksUsingCheckBox.contains(relationship)) { 146 return; 147 } 148 else { 149 for (ITask child : relationship.getChildren()) { 150 if (tasksUsingCheckBox.contains(child)) { 151 tasksUsingCheckBox.add(relationship); 152 return; 153 } 154 } 155 156 super.visit(relationship); 157 158 for (ITask child : relationship.getChildren()) { 159 if (tasksUsingCheckBox.contains(child)) { 160 tasksUsingCheckBox.add(relationship); 161 break; 162 } 163 } 164 } 165 } 166 167 @Override 168 public void visit(IMarkingTemporalRelationship relationship) { 169 if (tasksUsingCheckBox.contains(relationship)) { 170 return; 171 } 172 else { 173 if (tasksUsingCheckBox.contains(relationship.getMarkedTask())) { 174 tasksUsingCheckBox.add(relationship); 175 return; 176 } 177 178 super.visit(relationship); 179 180 if (tasksUsingCheckBox.contains(relationship.getMarkedTask())) { 181 tasksUsingCheckBox.add(relationship); 182 return; 183 } 184 } 185 } 186 }); 187 } 188 189 return tasksUsingCheckBox; 165 // get a value that is 1 if for one group usage there is on average one check box usage 166 // get a value of 0 if for one group usage there is on average two check box usages 167 168 int ratio = noOfGroupUsages > 0 ? 1000 * noOfSingleCheckBoxUsages / noOfGroupUsages : 0; 169 170 System.out.println(currentView + " " + ratio + " " + noOfGroupUsages + " " + 171 noOfSingleCheckBoxUsages); 172 173 UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity 174 (ratio, noOfEvents, -1); 175 176 if (intensity != null) { 177 Map<String, Object> parameters = new HashMap<String, Object>(); 178 parameters.put("allUsages", noOfGroupUsages); 179 parameters.put("singleUsages", noOfSingleCheckBoxUsages); 180 parameters.put("ratio", 100 * noOfSingleCheckBoxUsages / noOfGroupUsages); 181 parameters.put("radioButtons", group.getValue()); 182 183 results.addSmell 184 (intensity, UsabilitySmellDescription.CHECK_BOX_SINGLE_SELECTION, parameters); 185 } 186 } 190 187 } 191 188 -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DataEntryMethodChangeRule.java
r1960 r2042 29 29 import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection; 30 30 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; 31 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor; 32 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; 31 33 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; 32 34 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; … … 98 100 int ratio = getAverageDataEntryMethodChangeRatio((ISequence) task); 99 101 100 if ( ratio > 0) {102 if ((ratio > 0) && (getLeafNodes(task) > 2)) { 101 103 methodChangeRatios.put(task, ratio); 102 104 } … … 105 107 106 108 return methodChangeRatios; 109 } 110 111 /** 112 * 113 */ 114 private int getLeafNodes(ITask task) { 115 final int[] counter = new int[1]; 116 117 task.accept(new DefaultTaskTraversingVisitor() { 118 @Override 119 public void visit(IEventTask eventTask) { 120 counter[0]++; 121 } 122 }); 123 124 return counter[0]; 107 125 } 108 126 -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DefaultValueRule.java
r1941 r2042 29 29 30 30 import de.ugoe.cs.autoquest.eventcore.Event; 31 import de.ugoe.cs.autoquest.eventcore.gui.MouseClick; 31 32 import de.ugoe.cs.autoquest.eventcore.gui.TextInput; 32 33 import de.ugoe.cs.autoquest.eventcore.gui.ValueSelection; 33 34 import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel; 34 35 import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox; 36 import de.ugoe.cs.autoquest.eventcore.guimodel.IComboBox; 35 37 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 36 38 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec; … … 136 138 maxObserved = Math.max(maxObserved, selectedValue.getValue()); 137 139 138 if (mostOftenSelected.size() > 0) { 139 ListIterator<Object> iterator = mostOftenSelected.listIterator(); 140 while (iterator.hasNext()) { 141 if (selectedValues.get(iterator.next()) < selectedValue.getValue()) { 142 iterator.previous(); 143 iterator.add(selectedValue.getKey()); 144 145 while (mostOftenSelected.size() > 5) { 146 mostOftenSelected.removeLast(); 147 } 148 149 break; 150 } 151 } 152 } 153 else { 140 boolean added = false; 141 ListIterator<Object> iterator = mostOftenSelected.listIterator(); 142 while (iterator.hasNext()) { 143 if (selectedValues.get(iterator.next()) < selectedValue.getValue()) { 144 iterator.previous(); 145 iterator.add(selectedValue.getKey()); 146 added = true; 147 break; 148 } 149 } 150 151 if (!added) { 154 152 mostOftenSelected.add(selectedValue.getKey()); 153 } 154 155 while (mostOftenSelected.size() > 5) { 156 mostOftenSelected.removeLast(); 155 157 } 156 158 } … … 163 165 } 164 166 165 if ((expected.length == 1) ||167 if ((expected.length > 1) && 166 168 (new ChiSquareTest().chiSquareTest(expected, observed, 0.05))) 167 169 { … … 218 220 */ 219 221 private boolean isValueSelection(ITaskInstance instance) { 220 return (instance instanceof IEventTaskInstance) && 221 ((((IEventTaskInstance) instance).getEvent().getType() instanceof TextInput) || 222 (((IEventTaskInstance) instance).getEvent().getType() instanceof ValueSelection)); 222 if (instance instanceof IEventTaskInstance) { 223 Event event = ((IEventTaskInstance) instance).getEvent(); 224 225 if ((event.getType() instanceof TextInput) || 226 (event.getType() instanceof ValueSelection)) 227 { 228 return true; 229 } 230 231 if (("JFC".equals(event.getTarget().getPlatform())) && 232 (event.getTarget() instanceof ICheckBox) && 233 (event.getType() instanceof MouseClick)) 234 { 235 return true; 236 } 237 } 238 239 return false; 223 240 } 224 241 … … 439 456 ((ValueSelection<?>) valueChange.getEvent().getType()).getSelectedValue(); 440 457 441 if ((target instanceof IRadioButton) || (target instanceof ICheckBox)) { 458 if ((target.target instanceof IRadioButton) || 459 (target.target instanceof ICheckBox)) 460 { 442 461 selectedValue = selectedValue + " (" + target + ")"; 462 } 463 else if (target.target instanceof IComboBox) { 464 if (selectedValue == null) { 465 // this may have happened due to the recording issue that selected 466 // values of combo boxes are not logged correctly. In this case, 467 // pretend to have the a random value selected 468 selectedValue = "randomValueDueToRecordingBug_" + Math.random(); 469 } 470 } 471 } 472 else if (valueChange.getEvent().getType() instanceof MouseClick) { 473 if ((target.target instanceof IRadioButton) || 474 (target.target instanceof ICheckBox)) 475 { 476 selectedValue = target.toString(); 477 } 478 else { 479 throw new IllegalStateException("the implementation needs to be extended " + 480 "to fully support clicks as value changes"); 443 481 } 444 482 } -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/MisleadingClickCueRule.java
r1918 r2042 24 24 import de.ugoe.cs.autoquest.eventcore.gui.MouseClick; 25 25 import de.ugoe.cs.autoquest.eventcore.gui.MouseDoubleClick; 26 import de.ugoe.cs.autoquest.eventcore.guimodel.IButton; 26 27 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 27 28 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView; … … 65 66 UsabilityEvaluationResult results) 66 67 { 67 for (Map.Entry<IGUI View, Map<IGUIElement, Integer>> uselessClickCounter :68 for (Map.Entry<IGUIElement, Map<IGUIView, Integer>> uselessClickCounter : 68 69 statistics.getUselessClickCounters().entrySet()) 69 70 { 70 for (Map.Entry<IGUIElement, Integer> counter : uselessClickCounter.getValue().entrySet()) 71 { 72 int uselessClicks = counter.getValue(); 73 int noOfViewDisplays = statistics.getViewOpenedCount(uselessClickCounter.getKey()); 74 75 int ratio = Math.min(1000, 1000 * uselessClicks / noOfViewDisplays); 76 77 UsabilitySmellIntensity intensity = 78 UsabilitySmellIntensity.getIntensity(ratio, uselessClicks, -1); 79 80 if (intensity != null) { 81 Map<String, Object> parameters = new HashMap<String, Object>(); 82 parameters.put("noOfViewDisplays", noOfViewDisplays); 83 parameters.put("uselessClicks", uselessClicks); 84 parameters.put("element", counter.getKey()); 85 parameters.put("view", uselessClickCounter.getKey()); 86 87 results.addSmell 88 (intensity, UsabilitySmellDescription.MISLEADING_CLICK_CUE, parameters); 89 } 71 int uselessClicks = 0; 72 int noOfViewDisplays = 0; 73 74 for (Map.Entry<IGUIView, Integer> counter : uselessClickCounter.getValue().entrySet()) { 75 uselessClicks += counter.getValue(); 76 noOfViewDisplays += statistics.getViewOpenedCount(counter.getKey()); 77 } 78 79 int ratio = Math.min(1000, 1000 * uselessClicks / noOfViewDisplays); 80 81 UsabilitySmellIntensity intensity = 82 UsabilitySmellIntensity.getIntensity(ratio, uselessClicks, -1); 83 84 if (intensity != null) { 85 Map<String, Object> parameters = new HashMap<String, Object>(); 86 parameters.put("noOfViewDisplays", noOfViewDisplays); 87 parameters.put("uselessClicks", uselessClicks); 88 parameters.put("element", uselessClickCounter.getKey()); 89 90 results.addSmell 91 (intensity, UsabilitySmellDescription.MISLEADING_CLICK_CUE, parameters); 90 92 } 91 93 } … … 155 157 (target instanceof IText)) 156 158 { 157 return true; 159 // check if the parent is a button 160 IGUIElement parent = target; 161 while ((parent != null) && !(parent instanceof IButton)) { 162 parent = parent.getParent(); 163 } 164 165 return !(parent instanceof IButton); 158 166 } 159 167 else { … … 171 179 172 180 /** */ 173 private Map<IGUI View, Map<IGUIElement, Integer>> uselessClickCounters = new HashMap<>();181 private Map<IGUIElement, Map<IGUIView, Integer>> uselessClickCounters = new HashMap<>(); 174 182 175 183 /** … … 199 207 */ 200 208 private void addUselessClick(IEventTaskInstance eventTaskInstance) { 201 Map<IGUIElement, Integer> counterMap = uselessClickCounters.get 202 (((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView()); 209 IGUIElement target = (IGUIElement) eventTaskInstance.getEvent().getTarget(); 210 211 Map<IGUIView, Integer> counterMap = uselessClickCounters.get(target); 203 212 204 213 if (counterMap == null) { 205 214 counterMap = new HashMap<>(); 206 uselessClickCounters.put 207 (((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView(), counterMap); 208 } 209 210 Integer counter = counterMap.get(eventTaskInstance.getEvent().getTarget()); 215 uselessClickCounters.put(target, counterMap); 216 } 217 218 Integer counter = counterMap.get(target.getView()); 211 219 212 220 if (counter == null) { 213 counterMap.put( (IGUIElement) eventTaskInstance.getEvent().getTarget(), 1);221 counterMap.put(target.getView(), 1); 214 222 } 215 223 else { 216 counterMap.put( (IGUIElement) eventTaskInstance.getEvent().getTarget(), counter + 1);217 } 218 } 219 220 /** 221 * 222 */ 223 private Map<IGUI View, Map<IGUIElement, Integer>> getUselessClickCounters() {224 counterMap.put(target.getView(), counter + 1); 225 } 226 } 227 228 /** 229 * 230 */ 231 private Map<IGUIElement, Map<IGUIView, Integer>> getUselessClickCounters() { 224 232 return uselessClickCounters; 225 233 } -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/MissingFeedbackRule.java
r1918 r2042 195 195 196 196 if (clicksOnIdenticalButton != null) { 197 if (clicksOnIdenticalButton.size() > 1) { 198 //throw new IllegalStateException("not described in dissertation"); 199 } 200 197 201 long cummulativeImpatience = 0; 198 202 -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TextInputStatisticsRule.java
r1918 r2042 107 107 int noOfUsagesOfTextField1 = statistics.getUsageCount(entry.textField1); 108 108 int noOfUsagesOfTextField2 = statistics.getUsageCount(entry.textField2); 109 int noOfUsagesOfTextField1WithSameTextInTextField2 = entry.enteredTexts.size(); 110 111 int ratioTextField1 = 112 1000 * noOfUsagesOfTextField1WithSameTextInTextField2 / noOfUsagesOfTextField1; 113 114 int ratioTextField2 = 115 1000 * noOfUsagesOfTextField1WithSameTextInTextField2 / noOfUsagesOfTextField2; 116 117 createTextFieldEntryRepetitionSmell(ratioTextField1, entry.textField1, 118 entry.textField2, results); 119 120 createTextFieldEntryRepetitionSmell(ratioTextField2, entry.textField2, 121 entry.textField1, results); 122 123 } 124 } 125 126 /** 127 * 128 */ 129 private void createTextFieldEntryRepetitionSmell(int ratioOfEqualEntries, 130 ITextField textField1, 131 ITextField textField2, 132 UsabilityEvaluationResult results) 133 { 109 int numberOfEqualEntries = entry.enteredTexts.size(); 110 111 createTextFieldEntryRepetitionSmell(noOfUsagesOfTextField1, numberOfEqualEntries, 112 entry.textField1, entry.textField2, results); 113 114 createTextFieldEntryRepetitionSmell(noOfUsagesOfTextField2, numberOfEqualEntries, 115 entry.textField2, entry.textField1, results); 116 117 } 118 } 119 120 /** 121 * 122 */ 123 private void createTextFieldEntryRepetitionSmell(int allEntries, 124 int numberOfEqualEntries, 125 ITextField textField1, 126 ITextField textField2, 127 UsabilityEvaluationResult results) 128 { 129 int ratio = 1000 * numberOfEqualEntries / allEntries; 130 134 131 UsabilitySmellIntensity severity = 135 UsabilitySmellIntensity.getIntensity(ratio OfEqualEntries);132 UsabilitySmellIntensity.getIntensity(ratio, allEntries, -1); 136 133 137 134 if (severity != null) { 138 135 Map<String, Object> parameters = new HashMap<String, Object>(); 139 parameters.put("textRepetitionRatio", (ratioOfEqualEntries / 10)); 136 parameters.put("numberOfEqualEntries", numberOfEqualEntries); 137 parameters.put("numberOfAllEntries", allEntries); 138 parameters.put("textRepetitionRatio", (ratio / 10)); 140 139 parameters.put("textField1", textField1); 141 140 parameters.put("textField2", textField2); … … 169 168 int ratio = 1000 * noLetterOrDigitCount / allCharactersCount; 170 169 171 UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity(ratio); 170 UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity 171 (ratio, statistics.getAllInputsInto(textField).size(), -1); 172 172 173 173 if (severity != null) { -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UnusedGUIElementsRule.java
r1918 r2042 15 15 package de.ugoe.cs.autoquest.usability; 16 16 17 import java.util.ArrayList; 17 18 import java.util.HashMap; 18 19 import java.util.HashSet; … … 28 29 import de.ugoe.cs.autoquest.eventcore.guimodel.IComboBox; 29 30 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 31 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView; 30 32 import de.ugoe.cs.autoquest.eventcore.guimodel.IListBox; 31 33 import de.ugoe.cs.autoquest.eventcore.guimodel.IMenuButton; 32 34 import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea; 33 35 import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField; 36 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; 34 37 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; 35 38 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; … … 37 40 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; 38 41 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; 42 import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; 39 43 40 44 /** … … 54 58 public UsabilityEvaluationResult evaluate(ITaskModel taskModel) { 55 59 UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel); 56 57 GUIModel guiModel = getGUIModel(taskModel); 58 Set<IGUIElement> allGUIElements = getAllGUIElements(guiModel); 59 Set<IGUIElement> usedGUIElements = getUsedGUIElements(taskModel); 60 List<IGUIElement> unusedGUIElements = getUnusedGUIElements(usedGUIElements, allGUIElements); 61 handleUnusedGUIElements(unusedGUIElements, allGUIElements, results); 60 61 Map<IGUIView, List<Set<IGUIElement>>> viewDisplays = 62 getViewDisplays(taskModel.getUserSessions()); 63 64 Map<IGUIView, Set<IGUIElement>> allGUIElements = getAllGUIElements(taskModel); 65 66 for (Map.Entry<IGUIView, List<Set<IGUIElement>>> viewDisplay : viewDisplays.entrySet()) { 67 handleUnusedGUIElements 68 (allGUIElements, viewDisplay.getKey(), viewDisplay.getValue(), results); 69 } 62 70 63 71 return results; … … 65 73 66 74 /** 67 * <p> 68 * TODO: comment 69 * </p> 75 * @param results 70 76 * 71 * @param unusedGUIElements 72 * @param results 73 */ 74 private void handleUnusedGUIElements(List<IGUIElement> unusedGUIElements, 75 Set<IGUIElement> allGUIElements, 76 UsabilityEvaluationResult results) 77 */ 78 private void handleUnusedGUIElements(Map<IGUIView, Set<IGUIElement>> usedGUIElements, 79 IGUIView view, 80 List<Set<IGUIElement>> viewUsages, 81 UsabilityEvaluationResult results) 77 82 { 78 int ratio = 1000 * unusedGUIElements.size() / allGUIElements.size(); 79 80 UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity(ratio); 81 82 if (severity != null) { 83 Map<String, Object> parameters = new HashMap<String, Object>(); 84 85 parameters.put("ratio", ratio / 10); 86 parameters.put("noOfUnused", unusedGUIElements.size()); 87 parameters.put("noOfAll", allGUIElements.size()); 88 parameters.put("unusedGuiElements", unusedGUIElements); 89 90 results.addSmell 91 (severity, UsabilitySmellDescription.UNUSED_GUI_ELEMENTS, parameters); 92 } 93 } 94 95 /** 96 * <p> 97 * TODO: comment 98 * </p> 83 Set<IGUIElement> allElementsInView = usedGUIElements.get(view); 84 85 if (allElementsInView == null) { 86 return; 87 } 88 89 Map<Integer, List<IGUIElement>> usageCounters = new HashMap<>(); 90 91 for (IGUIElement relevantElement : allElementsInView) { 92 int usageCounter = 0; 93 94 for (Set<IGUIElement> viewUsage : viewUsages) { 95 if (viewUsage.contains(relevantElement)) { 96 usageCounter++; 97 } 98 } 99 100 List<IGUIElement> elementsWithSameUsage = usageCounters.get(usageCounter); 101 102 if (elementsWithSameUsage == null) { 103 elementsWithSameUsage = new LinkedList<>(); 104 usageCounters.put(usageCounter, elementsWithSameUsage); 105 } 106 107 elementsWithSameUsage.add(relevantElement); 108 } 109 110 int cumulativeGuiElementUsage = 0; 111 for (Set<IGUIElement> viewUsage : viewUsages) { 112 cumulativeGuiElementUsage += viewUsage.size(); 113 } 114 115 List<IGUIElement> unusedElements = usageCounters.get(0); 116 117 if (unusedElements != null) { 118 int ratio = 1000 * unusedElements.size() / allElementsInView.size(); 119 120 UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity 121 (ratio, cumulativeGuiElementUsage, -1); 122 123 if (severity != null) { 124 Map<String, Object> parameters = new HashMap<String, Object>(); 125 126 parameters.put("ratio", ratio / 10); 127 parameters.put("allDisplays", viewUsages.size()); 128 parameters.put("view", view); 129 parameters.put("unusedGuiElements", unusedElements); 130 parameters.put("allGuiElements", allElementsInView.size()); 131 132 results.addSmell 133 (severity, UsabilitySmellDescription.UNUSED_GUI_ELEMENTS, parameters); 134 } 135 } 136 } 137 138 /** 99 139 * 100 * @param taskModel101 * @return102 */103 private GUIModel getGUIModel(ITaskModel taskModel) {140 */ 141 private Map<IGUIView, Set<IGUIElement>> getAllGUIElements(ITaskModel taskModel) { 142 Map<IGUIView, Set<IGUIElement>> result = new HashMap<>(); 143 104 144 for (ITask task : taskModel.getTasks()) { 105 145 if (task instanceof IEventTask) { … … 107 147 Event event = ((IEventTaskInstance) instance).getEvent(); 108 148 109 if (event.getTarget() instanceof IGUIElement) { 110 return ((IGUIElement) event.getTarget()).getGUIModel(); 149 if ((event.getTarget() instanceof IGUIElement) && 150 (isRelevant((IGUIElement) event.getTarget()))) 151 { 152 IGUIView view = ((IGUIElement) event.getTarget()).getView(); 153 154 Set<IGUIElement> elements = result.get(view); 155 156 if (elements == null) { 157 elements = new HashSet<>(); 158 result.put(view, elements); 159 } 160 161 elements.add((IGUIElement) event.getTarget()); 111 162 } 112 163 } … … 114 165 } 115 166 116 return null; 117 } 118 119 /** 120 * <p> 121 * TODO: comment 122 * </p> 123 * 124 * @param taskModel 125 */ 126 private Set<IGUIElement> getUsedGUIElements(ITaskModel taskModel) { 127 Set<IGUIElement> usedGUIElements = new HashSet<IGUIElement>(); 128 129 for (ITask task : taskModel.getTasks()) { 130 if (task instanceof IEventTask) { 131 for (ITaskInstance instance : task.getInstances()) { 132 Event event = ((IEventTaskInstance) instance).getEvent(); 133 134 if (event.getTarget() instanceof IGUIElement) { 135 usedGUIElements.add((IGUIElement) event.getTarget()); 167 // the problem is, that using the GUI model does not allow to find all in a specific view 168 // as the GUI model may return a merged element instead. But anyway, we can add those, which 169 // are in the GUI model and have the same view. 170 171 GUIModel model = result.values().iterator().next().iterator().next().getGUIModel(); 172 173 GUIModel.Traverser traverser = model.getTraverser(); 174 175 IGUIElement currentGUIElement = null; 176 do { 177 if (traverser.hasFirstChild()) { 178 currentGUIElement = traverser.firstChild(); 179 } 180 else if (traverser.hasNextSibling()) { 181 currentGUIElement = traverser.nextSibling(); 182 } 183 else { 184 while (currentGUIElement != null) { 185 currentGUIElement = traverser.parent(); 186 if (traverser.hasNextSibling()) { 187 currentGUIElement = traverser.nextSibling(); 188 break; 136 189 } 137 190 } 138 191 } 139 } 140 141 return usedGUIElements; 142 } 143 144 /** 145 * <p> 146 * TODO: comment 147 * </p> 148 * 149 * @param taskModel 150 */ 151 private Set<IGUIElement> getAllGUIElements(GUIModel guiModel) { 152 Set<IGUIElement> allGUIElements = new HashSet<IGUIElement>(); 153 154 if (guiModel != null) { 155 GUIModel.Traverser traverser = guiModel.getTraverser(); 156 157 IGUIElement currentGUIElement = null; 158 do { 159 if (traverser.hasFirstChild()) { 160 currentGUIElement = traverser.firstChild(); 161 } 162 else if (traverser.hasNextSibling()) { 163 currentGUIElement = traverser.nextSibling(); 164 } 165 else { 166 while (currentGUIElement != null) { 167 currentGUIElement = traverser.parent(); 168 if (traverser.hasNextSibling()) { 169 currentGUIElement = traverser.nextSibling(); 170 break; 192 193 if (isRelevant(currentGUIElement)) { 194 IGUIView view = currentGUIElement.getView(); 195 196 Set<IGUIElement> elements = result.get(view); 197 198 if (elements == null) { 199 elements = new HashSet<>(); 200 result.put(view, elements); 201 } 202 203 elements.add(currentGUIElement); 204 } 205 } 206 while (currentGUIElement != null); 207 208 return result; 209 } 210 211 /** 212 * 213 */ 214 private Map<IGUIView, List<Set<IGUIElement>>> getViewDisplays(List<IUserSession> sessions) { 215 final IGUIView[] currentView = new IGUIView[1]; 216 final List<IEventTaskInstance> actionInstances = new ArrayList<>(); 217 final Map<IGUIView, List<Set<IGUIElement>>> result = new HashMap<>(); 218 219 for (IUserSession session : sessions) { 220 currentView[0] = null; 221 actionInstances.clear(); 222 223 for (final ITaskInstance currentRoot : session) { 224 currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() { 225 @Override 226 public void visit(IEventTaskInstance eventTaskInstance) { 227 if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) { 228 IGUIView view = 229 ((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView(); 230 231 if ((currentView[0] == null) && (view != null)) { 232 currentView[0] = view; 233 actionInstances.clear(); 234 } 235 else if ((currentView[0] != null) && (!currentView[0].equals(view))) { 236 addRelevantTargets(currentView[0], actionInstances, result); 237 238 currentView[0] = view; 239 actionInstances.clear(); 240 } 241 } 242 243 if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) { 244 actionInstances.add(eventTaskInstance); 171 245 } 172 246 } 173 } 174 175 if (isRelevant(currentGUIElement)) { 176 allGUIElements.add(currentGUIElement); 177 } 178 } 179 while (currentGUIElement != null); 180 } 181 182 return allGUIElements; 183 } 184 185 /** 186 * <p> 187 * TODO: comment 188 * </p> 247 }); 248 } 249 250 // add the used GUI elements of the last shown view in the session 251 if (currentView[0] != null) { 252 addRelevantTargets(currentView[0], actionInstances, result); 253 } 254 } 255 256 return result; 257 } 258 259 /** 189 260 * 190 * @param taskModel191 */192 private List<IGUIElement> getUnusedGUIElements(Set<IGUIElement> usedGUIElements,193 Set<IGUIElement> allGUIElements)261 */ 262 private void addRelevantTargets(IGUIView view, 263 List<IEventTaskInstance> actionInstances, 264 Map<IGUIView, List<Set<IGUIElement>>> result) 194 265 { 195 List<IGUIElement> unusedGUIElements = new LinkedList<IGUIElement>(); 196 for (IGUIElement currentGUIElement : allGUIElements) { 197 if (isRelevant(currentGUIElement) && 198 !belongsToUsedGUIElements(currentGUIElement, usedGUIElements)) 199 { 200 unusedGUIElements.add(currentGUIElement); 201 } 202 } 203 204 return unusedGUIElements; 205 } 206 207 /** 208 * <p> 209 * TODO: comment 210 * </p> 266 List<Set<IGUIElement>> usedGUIElements = result.get(view); 267 268 if (usedGUIElements == null) { 269 usedGUIElements = new LinkedList<>(); 270 result.put(view, usedGUIElements); 271 } 272 273 Set<IGUIElement> elementsInViewDisplay = new HashSet<>(); 274 275 for (IEventTaskInstance actionInstance : actionInstances) { 276 IGUIElement element = (IGUIElement) actionInstance.getEvent().getTarget(); 277 278 while (element != null) { 279 if (isRelevant(element)) { 280 elementsInViewDisplay.add(element); 281 } 282 283 element = element.getParent(); 284 } 285 } 286 287 usedGUIElements.add(elementsInViewDisplay); 288 } 289 290 /** 211 291 * 212 * @param currentGUIElement213 * @param usedGUIElements214 * @return215 */216 private boolean belongsToUsedGUIElements(IGUIElement relevantGUIElement,217 Set<IGUIElement> usedGUIElements)218 {219 if (usedGUIElements.contains(relevantGUIElement)) {220 return true;221 }222 else {223 // in some cases, the events are recorded for the children of the relevant GUI elements224 // therefore, check the children, as well.225 List<IGUIElement> children =226 relevantGUIElement.getGUIModel().getChildren(relevantGUIElement);227 228 if (children != null) {229 for (IGUIElement child : children) {230 if (belongsToUsedGUIElements(child, usedGUIElements)) {231 return true;232 }233 }234 }235 }236 237 return false;238 }239 240 /**241 * <p>242 * TODO: comment243 * </p>244 *245 * @param currentGUIElement246 * @return247 292 */ 248 293 private boolean isRelevant(IGUIElement currentGUIElement) { -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationManager.java
r1918 r2042 16 16 17 17 import java.util.ArrayList; 18 import java.util.HashMap; 19 import java.util.LinkedList; 18 20 import java.util.List; 21 import java.util.ListIterator; 22 import java.util.Map; 19 23 import java.util.logging.Level; 20 24 … … 48 52 */ 49 53 private void init() { 50 rules.add(new TaskTreeTestRule()); 51 // rules.add(new TextInputStatisticsRule()); 52 // rules.add(new MissingFeedbackRule()); 53 // rules.add(new EventCoverageRatioRule()); 54 // rules.add(new TargetDistanceRule()); 55 // rules.add(new RequiredInefficientActionsRule()); 56 // rules.add(new DataEntryMethodChangeRule()); 54 // rules.add(new TaskTreeTestRule()); 55 56 rules.add(new EventCoverageRatioRule()); 57 rules.add(new RequiredInefficientActionsRule()); 58 rules.add(new TargetDistanceRule()); 59 rules.add(new MissingFeedbackRule()); 60 rules.add(new DataEntryMethodChangeRule()); 61 rules.add(new CommonTaskRateRule()); 62 rules.add(new TextInputStatisticsRule()); 63 rules.add(new CheckBoxMultipleSelectionRule()); 64 rules.add(new MisleadingClickCueRule()); 65 rules.add(new DefaultCursorPositioningRule()); 57 66 rules.add(new DefaultValueRule()); 58 // rules.add(new CheckBoxMultipleSelectionRule()); 59 // rules.add(new CommonTaskRateRule()); 60 // rules.add(new MisleadingClickCueRule()); 61 // rules.add(new DefaultCursorPositioningRule()); 62 // rules.add(new UnusedGUIElementsRule()); 67 rules.add(new UnusedGUIElementsRule()); 68 63 69 // rules.add(new TaskCooccurrenceRule()); 64 70 } … … 67 73 * 68 74 */ 69 public UsabilityEvaluationResult evaluateUsability(ITaskModel taskModel ) {75 public UsabilityEvaluationResult evaluateUsability(ITaskModel taskModel, int maxCount) { 70 76 Console.traceln(Level.INFO, "evaluating usability of task model " + taskModel); 71 77 … … 73 79 74 80 for (UsabilityEvaluationRule rule : rules) { 75 Console.traceln(Level.INFO, " applying rule " + rule.getClass().getSimpleName());81 Console.traceln(Level.INFO, "\napplying rule " + rule.getClass().getSimpleName()); 76 82 UsabilityEvaluationResult result = rule.evaluate(taskModel); 77 interimResults.add(result); 78 Console.traceln(Level.INFO, "the rule found " + result.getAllSmells().size() + 79 " usability smells."); 80 81 List<ITask> referredTasks = new ArrayList<ITask>(); 82 83 84 Map<String, List<UsabilitySmell>> smellGroups = new HashMap<>(); 85 83 86 for (UsabilitySmell smell : result.getAllSmells()) { 84 if (smell.getSmellingTask() != null) { 85 referredTasks.add(smell.getSmellingTask()); 86 } 87 } 88 89 int counter = 0; 90 for (int i = 0; i < referredTasks.size(); i++) { 91 for (int j = 0; j < referredTasks.size(); j++) { 92 if (isChildOf(referredTasks.get(i), referredTasks.get(j))) { 93 counter++; 94 break; 87 List<UsabilitySmell> smellGroup = smellGroups.get(smell.getBriefDescription()); 88 89 if (smellGroup == null) { 90 smellGroup = new LinkedList<>(); 91 smellGroups.put(smell.getBriefDescription(), smellGroup); 92 } 93 94 smellGroup.add(smell); 95 } 96 97 for (Map.Entry<String, List<UsabilitySmell>> smellGroup : smellGroups.entrySet()) { 98 Console.traceln(Level.INFO, "the rule found " + smellGroup.getValue().size() + 99 " usability smells of type \"" + smellGroup.getKey() + "\""); 100 101 result = new UsabilityEvaluationResult(taskModel, smellGroup.getValue()); 102 103 checkDuplicates(result); 104 105 if (maxCount < result.getAllSmells().size()) { 106 Console.traceln(Level.INFO, "filtering for " + maxCount + 107 " smells of same type with highest event coverage."); 108 109 LinkedList<UsabilitySmell> sortedSmells = new LinkedList<>(); 110 111 for (UsabilitySmell smell : result.getAllSmells()) { 112 ListIterator<UsabilitySmell> iterator = sortedSmells.listIterator(); 113 114 boolean added = false; 115 116 while (iterator.hasNext()) { 117 if (iterator.next().getIntensity().getEventCoverage() < 118 smell.getIntensity().getEventCoverage()) 119 { 120 iterator.previous(); 121 iterator.add(smell); 122 added = true; 123 break; 124 } 125 } 126 127 if (!added) { 128 sortedSmells.add(smell); 129 } 130 131 while (sortedSmells.size() > maxCount) { 132 sortedSmells.removeLast(); 133 } 95 134 } 96 } 97 } 98 99 if (counter > 0) { 100 Console.traceln(Level.INFO, counter + " of the findings are duplicates in " + 101 "that they refer to tasks whose parent tasks are also " + 102 "referred by the findings"); 135 136 result = new UsabilityEvaluationResult(taskModel, sortedSmells); 137 checkDuplicates(result); 138 } 139 140 interimResults.add(result); 103 141 } 104 142 } … … 112 150 113 151 /** 114 * <p>115 * TODO: comment116 * </p>117 152 * 118 * @param iTask 119 * @param iTask2 120 * @return 153 */ 154 private void checkDuplicates(UsabilityEvaluationResult result) { 155 List<ITask> referredTasks = new ArrayList<ITask>(); 156 157 for (UsabilitySmell smell : result.getAllSmells()) { 158 if (smell.getSmellingTask() != null) { 159 referredTasks.add(smell.getSmellingTask()); 160 } 161 } 162 163 int counter = 0; 164 for (int i = 0; i < referredTasks.size(); i++) { 165 for (int j = 0; j < referredTasks.size(); j++) { 166 if (isChildOf(referredTasks.get(i), referredTasks.get(j))) { 167 counter++; 168 break; 169 } 170 } 171 } 172 173 if (counter > 0) { 174 Console.traceln(Level.INFO, counter + " of the findings are duplicates in " + 175 "that they refer to tasks whose parent tasks are also " + 176 "referred by the findings"); 177 } 178 } 179 180 /** 181 * 121 182 */ 122 183 private boolean isChildOf(final ITask potChild, ITask potParent) { -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationResult.java
r1918 r2042 16 16 17 17 import java.util.ArrayList; 18 import java.util.Collection; 18 19 import java.util.List; 19 20 import java.util.Map; … … 41 42 public UsabilityEvaluationResult(ITaskModel taskModel) { 42 43 this.taskModel = taskModel; 44 } 45 46 /** 47 * 48 */ 49 public UsabilityEvaluationResult(ITaskModel taskModel, 50 Collection<UsabilitySmell> smells) 51 { 52 this.taskModel = taskModel; 53 for (UsabilitySmell smell : smells) { 54 this.smells.add(smell); 55 } 43 56 } 44 57
Note: See TracChangeset
for help on using the changeset viewer.