Robot Framework Integrated Development Environment (RIDE)
searchtests.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 from functools import (total_ordering, cmp_to_key)
17 
18 import wx
19 
20 from .. import robotapi
21 from ..action import ActionInfo
22 from ..pluginapi import Plugin
23 from ..publish import RideOpenTagSearch
24 from .dialogsearchtests import TestsDialog
25 from ..widgets import ImageProvider
26 
27 
28 @total_ordering
29 
31  HEADER = 'Search Tests'
32 
35  _selection = None
36 
37  def enable(self):
38  self.register_actionregister_action(ActionInfo(
39  'Tools', self.HEADERHEADER, self.show_empty_searchshow_empty_search,
40  shortcut='F3', doc=self.__doc____doc__,
41  icon=ImageProvider().TEST_SEARCH_ICON, position=50))
42  self.register_search_actionregister_search_action(
43  self.HEADERHEADER, self.show_search_forshow_search_for,
44  ImageProvider().TEST_SEARCH_ICON, default=True)
45  self.subscribesubscribe(self.show_tag_searchshow_tag_search, RideOpenTagSearch)
46  self._dialog_dialog = None
47 
48  def show_search_for(self, text):
49  if self._dialog_dialog is None:
50  self._create_tests_dialog_create_tests_dialog()
51  self._dialog_dialog.set_search_model(
52  text, self._search_results_search_results(TestSearchMatcher(text)))
53  self._dialog_dialog.set_focus_to_default_location()
54 
55  def show_search_for_tag_patterns(self, includes, excludes):
56  matcher = TagSearchMatcher(includes, excludes)
57  self._dialog_dialog.set_tag_search_model(
58  includes, excludes, self._search_results_search_results(matcher))
59  self._dialog_dialog.set_focus_to_default_location()
60 
61  def show_tag_search(self, message):
62  if self._dialog_dialog is None:
63  self._create_tests_dialog_create_tests_dialog()
64  self.show_search_for_tag_patternsshow_search_for_tag_patterns(message.includes, message.excludes)
65  self._dialog_dialog._select_page(1)
66 
68  self._dialog_dialog = TestsDialog(
69  fuzzy_search_handler=self.show_search_forshow_search_for,
70  tag_search_handler=self.show_search_for_tag_patternsshow_search_for_tag_patterns,
71  add_to_selected_handler=self._add_to_selected_add_to_selected)
72  self._dialog_dialog.add_selection_listener(self._selected_selected)
73  self._dialog_dialog.Bind(wx.EVT_CLOSE, self._dialog_closed_dialog_closed)
74  self._selected_timer_selected_timer = wx.Timer(self._dialog_dialog)
75  self._dialog_dialog.Bind(wx.EVT_TIMER, self._do_with_selection_do_with_selection)
76  self._dialog_dialog.Show()
77 
78  def _add_to_selected(self, tests):
79  self.treetree.SelectTests(tests)
80 
81  def _dialog_closed(self, event):
82  self._dialog_dialog = None
83  event.Skip()
84 
85  def show_empty_search(self, event):
86  self.show_search_forshow_search_for('')
87 
88  def _do_with_selection(self, evt=None):
89  test, match_location = self._selection_selection
90  self.treetree.select_node_by_data(test)
91  self._dialog_dialog.set_focus_to_default_location(test)
92 
93  def _selected(self, selection):
94  self._selection_selection = selection
95  self._selected_timer_selected_timer.Start(400, True)
96 
97  def _search_results(self, matcher):
98  current_suite = self.frameframe._controller.data
99  if not current_suite:
100  return []
101  result = self._search_search(matcher, current_suite)
102  return sorted(result, key=cmp_to_key(lambda x, y:
103  self.m_cmpm_cmp(x[1], y[1])))
104 
105  def _search(self, matcher, data):
106  for test in data.tests:
107  match = matcher.matches(test)
108  if match:
109  yield test, match
110  for s in data.suites:
111  for test, match in self._search_search(matcher, s):
112  yield test, match
113 
114  def disable(self):
115  self.unregister_actionsunregister_actions()
116 
117  @staticmethod
118  def m_cmp(a, b):
119  return (a > b) - (a < b)
120 
121  def __eq__(self, other):
122  return self.namename.lower() == other.name.lower()
123 
124  def __hash__(self):
125  return hash(repr(self))
126 
127  def __lt__(self, other):
128  return self.namename.lower() < other.name.lower()
129 
130 
132 
133  def __init__(self, includes, excludes):
134  self._tag_pattern_includes_tag_pattern_includes = robotapi.TagPatterns(
135  includes.split()) if includes.split() else None
136  self._tag_pattern_excludes_tag_pattern_excludes = robotapi.TagPatterns(excludes.split())
137 
138  def matches(self, test):
139  tags = [str(tag) for tag in test.tags]
140  if self._matches_matches(tags):
141  return test.longname
142  return False
143 
144  def _matches(self, tags):
145  return (self._tag_pattern_includes_tag_pattern_includes is None or
146  self._tag_pattern_includes_tag_pattern_includes.match(tags)) and \
147  not self._tag_pattern_excludes_tag_pattern_excludes.match(tags)
148 
149 
151 
152  def __init__(self, text):
153  self._texts_texts = text.split()
154  self._texts_lower_texts_lower = [t.lower() for t in self._texts_texts]
155 
156  def matches(self, test):
157  if self._matches_matches(test):
158  return SearchResult(self._texts_texts, self._texts_lower_texts_lower, test)
159  return False
160 
161  def _matches(self, test):
162  name = test.name.lower()
163  if self._match_in_match_in(name):
164  return True
165  if any(self._match_in_match_in(str(tag).lower()) for tag in test.tags):
166  return True
167  doc = test.documentation.value.lower()
168  if self._match_in_match_in(doc):
169  return True
170  return False
171 
172  def _match_in(self, text):
173  return any(word in text for word in self._texts_lower_texts_lower)
174 
175 
176 class SearchResult():
177 
178  def __init__(self, original_search_terms, search_terms_lower, test):
179  self._original_search_terms_original_search_terms = original_search_terms
180  self._search_terms_lower_search_terms_lower = search_terms_lower
181  self._test_test = test
182  self.__total_matches__total_matches = None
183  self.__tags__tags = None
184 
185  # This is ignored on Python 3
186  """
187  def __cmp__(self, other):
188  """
189  def _helper_cmp_(self, other):
190  totals, other_totals = self._total_matches_total_matches(), other._total_matches()
191  if totals != other_totals:
192  return self.m_cmpm_cmp(other_totals, totals)
193  names = self._compare_compare(
194  self._is_name_match_is_name_match(), other._is_name_match(),
195  self._test_test.name, other._test.name)
196  if names:
197  return names
198  tags = self._compare_compare(
199  self._is_tag_match_is_tag_match(), other._is_tag_match(),
200  self._tags_tags(), other._tags())
201  if tags:
202  return tags
203  return self.m_cmpm_cmp(self._test_test.name, other._test.name)
204 
205  def _compare(self, my_result, other_result, my_cmp, other_cmp):
206  if my_result and not other_result:
207  return -1
208  if not my_result and other_result:
209  return 1
210  if my_result and other_result:
211  return self.m_cmpm_cmp(my_cmp, other_cmp)
212  return 0
213 
214  def _total_matches(self):
215  if not self.__total_matches__total_matches:
216  self.__total_matches__total_matches = sum(
217  1 for word in self._search_terms_lower_search_terms_lower
218  if word in self._test_test.name.lower()
219  or any(word in t for t in self._tags_tags())
220  or word in self._test_test.documentation.value.lower())
221  return self.__total_matches__total_matches
222 
223  def _match_in(self, text):
224  return any(word in text for word in self._search_terms_lower_search_terms_lower)
225 
226  def _is_name_match(self):
227  return self._match_in_match_in(self._test_test.name.lower())
228 
229  def _is_tag_match(self):
230  return any(self._match_in_match_in(t) for t in self._tags_tags())
231 
232  def _tags(self):
233  if self.__tags__tags is None:
234  self.__tags__tags = [str(tag).lower() for tag in self._test_test.tags]
235  return self.__tags__tags
236 
237  def __repr__(self):
238  return self._test_test.name
239 
240  @staticmethod
241  def m_cmp(a, b):
242  return (a > b) - (a < b)
243 
244  def __eq__(self, other):
245  return self.__hash____hash__() == other.__hash__()
246 
247  def __ne__(self, other):
248  return not self.__eq____eq__(other)
249 
250  def __hash__(self):
251  return hash(repr(self))
252 
253  def __lt__(self, other):
254  return self._helper_cmp__helper_cmp_(other) == -1
255 
256  def __le__(self, other):
257  return self._helper_cmp__helper_cmp_(other) <= 0
258 
259  def __gt__(self, other):
260  return self._helper_cmp__helper_cmp_(other) == 1
261 
262  def __ge__(self, other):
263  return self._helper_cmp__helper_cmp_(other) >= 0
Used to create menu entries, keyboard shortcuts and/or toolbar buttons.
Definition: actioninfo.py:176
Entry point to RIDE plugin API – all plugins must extend this class.
Definition: plugin.py:50
def subscribe(self, listener, *topics)
Start to listen to messages with the given topics.
Definition: plugin.py:396
def register_action(self, action_info)
Registers a menu entry and optionally a shortcut and a toolbar icon.
Definition: plugin.py:205
def unregister_actions(self)
Unregisters all actions registered by this plugin.
Definition: plugin.py:230
def register_search_action(self, description, handler, icon, default=False)
Definition: plugin.py:226
def __init__(self, original_search_terms, search_terms_lower, test)
Definition: searchtests.py:178
def _compare(self, my_result, other_result, my_cmp, other_cmp)
Definition: searchtests.py:205
A plugin for searching tests based on name, tags and documentation.
Definition: searchtests.py:30
def enable(self)
This method is called by RIDE when the plugin is enabled.
Definition: searchtests.py:37
def disable(self)
Called by RIDE when the plugin is disabled.
Definition: searchtests.py:114
def show_search_for_tag_patterns(self, includes, excludes)
Definition: searchtests.py:55