11# Test methods with long descriptive names can omit docstrings
2- # pylint: disable=missing-docstring
3-
2+ # pylint: disable=all
43from unittest import TestCase
54import numpy as np
5+ from numpy .testing import assert_array_equal
66
77from AnyQt .QtCore import QModelIndex , QItemSelectionModel , Qt
8-
9- from Orange .data import ContinuousVariable , DiscreteVariable , \
10- StringVariable , TimeVariable , Table , Domain
11- from Orange .widgets .data .oweditdomain import EditDomainReport , OWEditDomain , \
12- ContinuousVariableEditor , DiscreteVariableEditor , VariableEditor , \
13- TimeVariableEditor
8+ from AnyQt .QtWidgets import QAction
9+ from AnyQt .QtTest import QTest
10+
11+ from Orange .data import (
12+ ContinuousVariable , DiscreteVariable , StringVariable , TimeVariable ,
13+ Table , Domain
14+ )
15+ from Orange .preprocess .transformation import Identity , Lookup
16+ from Orange .widgets .data .oweditdomain import (
17+ OWEditDomain ,
18+ ContinuousVariableEditor , DiscreteVariableEditor , VariableEditor ,
19+ TimeVariableEditor , Categorical , Real , Time , String ,
20+ Rename , Annotate , CategoriesMapping , report_transform ,
21+ apply_transform
22+ )
1423from Orange .widgets .data .owcolor import OWColor , ColorRole
1524from Orange .widgets .tests .base import WidgetTest , GuiTest
1625
17- SECTION_NAME = "NAME"
1826
19-
20- class TestEditDomainReport (TestCase ):
21- # This tests test private methods
22- # pylint: disable=protected-access
23-
24- def setUp (self ):
25- self .report = EditDomainReport ([], [])
26-
27- def test_section_yields_nothing_for_no_changes (self ):
28- result = self .report ._section (SECTION_NAME , [])
29- self .assertEmpty (result )
30-
31- def test_section_yields_header_for_changes (self ):
32- result = self .report ._section (SECTION_NAME , ["a" ])
33- self .assertTrue (any (SECTION_NAME in item for item in result ))
34-
35- def test_value_changes_yields_nothing_for_no_change (self ):
36- a = DiscreteVariable ("a" , values = "abc" )
37- self .assertEmpty (self .report ._value_changes (a , a ))
38-
39- def test_value_changes_yields_nothing_for_continuous_variables (self ):
40- v1 , v2 = ContinuousVariable ("a" ), ContinuousVariable ("b" )
41- self .assertEmpty (self .report ._value_changes (v1 , v2 ))
42-
43- def test_value_changes_yields_changed_values (self ):
44- v1 , v2 = DiscreteVariable ("a" , "ab" ), DiscreteVariable ("b" , "ac" )
45- self .assertNotEmpty (self .report ._value_changes (v1 , v2 ))
46-
47- def test_label_changes_yields_nothing_for_no_change (self ):
48- v1 = ContinuousVariable ("a" )
49- v1 .attributes ["a" ] = "b"
50- self .assertEmpty (self .report ._value_changes (v1 , v1 ))
51-
52- def test_label_changes_yields_added_labels (self ):
53- v1 = ContinuousVariable ("a" )
54- v2 = v1 .copy (None )
55- v2 .attributes ["a" ] = "b"
56- self .assertNotEmpty (self .report ._label_changes (v1 , v2 ))
57-
58- def test_label_changes_yields_removed_labels (self ):
59- v1 = ContinuousVariable ("a" )
60- v1 .attributes ["a" ] = "b"
61- v2 = v1 .copy (None )
62- del v2 .attributes ["a" ]
63- self .assertNotEmpty (self .report ._label_changes (v1 , v2 ))
64-
65- def test_label_changes_yields_modified_labels (self ):
66- v1 = ContinuousVariable ("a" )
67- v1 .attributes ["a" ] = "b"
68- v2 = v1 .copy (None )
69- v2 .attributes ["a" ] = "c"
70- self .assertNotEmpty (self .report ._label_changes (v1 , v2 ))
71-
72- def assertEmpty (self , iterable ):
73- self .assertRaises (StopIteration , lambda : next (iter (iterable )))
74-
75- def assertNotEmpty (self , iterable ):
76- try :
77- next (iter (iterable ))
78- except StopIteration :
79- self .fail ("Iterator did not produce any lines" )
27+ class TestReport (TestCase ):
28+ def test_rename (self ):
29+ var = Real ("X" , (- 1 , "" ), ())
30+ tr = Rename ("Y" )
31+ val = report_transform (var , [tr ])
32+ self .assertIn ("X" , val )
33+ self .assertIn ("Y" , val )
34+
35+ def test_annotate (self ):
36+ var = Real ("X" , (- 1 , "" ), (("a" , "1" ), ("b" , "z" )))
37+ tr = Annotate ((("a" , "2" ), ("j" , "z" )))
38+ r = report_transform (var , [tr ])
39+ self .assertIn ("a" , r )
40+ self .assertIn ("b" , r )
41+
42+ def test_categories_mapping (self ):
43+ var = Categorical ("C" , ("a" , "b" , "c" ), None , ())
44+ tr = CategoriesMapping (
45+ (("a" , "aa" ),
46+ ("b" , None ),
47+ ("c" , "cc" ),
48+ (None , "ee" )),
49+ )
50+ r = report_transform (var , [tr ])
51+ self .assertIn ("a" , r )
52+ self .assertIn ("aa" , r )
53+ self .assertIn ("b" , r )
54+ self .assertIn ("<s>" , r )
8055
8156
8257class TestOWEditDomain (WidgetTest ):
@@ -137,7 +112,7 @@ def test_list_attributes_remain_lists(self):
137112 idx = editor .labels_model .index (0 , 1 )
138113 editor .labels_model .setData (idx , "[1, 2, 4]" , Qt .EditRole )
139114
140- self .widget .unconditional_commit ()
115+ self .widget .commit ()
141116 t2 = self .get_output (self .widget .Outputs .data )
142117 self .assertEqual (t2 .domain ["a" ].attributes ["list" ], [1 , 2 , 4 ])
143118
@@ -156,15 +131,20 @@ def test_duplicate_names(self):
156131 self .widget .domain_view .setCurrentIndex (idx )
157132 editor = self .widget .editor_stack .findChild (ContinuousVariableEditor )
158133
159- editor .name_edit .setText ("iris" )
160- editor .commit ()
134+ def enter_text (widget , text ):
135+ # type: (QLineEdit, str) -> None
136+ widget .selectAll ()
137+ QTest .keyClick (widget , Qt .Key_Delete )
138+ QTest .keyClicks (widget , text )
139+ QTest .keyClick (widget , Qt .Key_Return )
140+
141+ enter_text (editor .name_edit , "iris" )
161142 self .widget .commit ()
162143 self .assertTrue (self .widget .Error .duplicate_var_name .is_shown ())
163144 output = self .get_output (self .widget .Outputs .data )
164145 self .assertIsNone (output )
165146
166- editor .name_edit .setText ("sepal height" )
167- editor .commit ()
147+ enter_text (editor .name_edit , "sepal height" )
168148 self .widget .commit ()
169149 self .assertFalse (self .widget .Error .duplicate_var_name .is_shown ())
170150 output = self .get_output (self .widget .Outputs .data )
@@ -176,10 +156,12 @@ def test_time_variable_preservation(self):
176156 self .send_signal (self .widget .Inputs .data , table )
177157 output = self .get_output (self .widget .Outputs .data )
178158 self .assertEqual (str (table [0 , 4 ]), str (output [0 , 4 ]))
159+ view = self .widget .variables_view
160+ view .setCurrentIndex (view .model ().index (4 ))
179161
180162 editor = self .widget .editor_stack .findChild (TimeVariableEditor )
181163 editor .name_edit .setText ("Date" )
182- editor .commit ()
164+ editor .variable_changed . emit ()
183165 self .widget .commit ()
184166 output = self .get_output (self .widget .Outputs .data )
185167 self .assertEqual (str (table [0 , 4 ]), str (output [0 , 4 ]))
@@ -188,54 +170,68 @@ def test_time_variable_preservation(self):
188170class TestEditors (GuiTest ):
189171 def test_variable_editor (self ):
190172 w = VariableEditor ()
191- self .assertIs (w .get_data (), None )
173+ self .assertEqual (w .get_data (), ( None , []) )
192174
193- v = StringVariable (name = "S" )
194- v .attributes .update ({"A" : 1 , "B" : "b" },)
195- w .set_data (v )
175+ v = String ("S" , (("A" , "1" ), ("B" , "b" )))
176+ w .set_data (v , [])
196177
197178 self .assertEqual (w .name_edit .text (), v .name )
198- self .assertEqual (w .labels_model .get_dict (), v .attributes )
199- self .assertTrue (w .is_same ())
179+ self .assertEqual (w .labels_model .get_dict (),
180+ {"A" : "1" , "B" : "b" })
181+ self .assertEqual (w .get_data (), (v , []))
200182
201183 w .set_data (None )
202184 self .assertEqual (w .name_edit .text (), "" )
203185 self .assertEqual (w .labels_model .get_dict (), {})
204- self .assertIs (w .get_data (), None )
186+ self .assertEqual (w .get_data (), (None , []))
187+
188+ w .set_data (v , [Rename ("T" ), Annotate ((("a" , "1" ), ("b" , "2" )))])
189+ self .assertEqual (w .name_edit .text (), "T" )
190+ self .assertEqual (w .labels_model .rowCount (), 2 )
191+ add = w .findChild (QAction , "action-add-label" )
192+ add .trigger ()
193+ remove = w .findChild (QAction , "action-delete-label" )
194+ remove .trigger ()
205195
206196 def test_continuous_editor (self ):
207197 w = ContinuousVariableEditor ()
208- self .assertIs (w .get_data (), None )
198+ self .assertEqual (w .get_data (), ( None , []) )
209199
210- v = ContinuousVariable ("X" , number_of_decimals = 5 )
211- v .attributes .update ({"A" : 1 , "B" : "b" })
212- w .set_data (v )
200+ v = Real ("X" , (- 1 , "" ), (("A" , "1" ), ("B" , "b" )))
201+ w .set_data (v , [])
213202
214203 self .assertEqual (w .name_edit .text (), v .name )
215- self .assertEqual (w .labels_model .get_dict (), v .attributes )
216- self .assertTrue (w .is_same ())
204+ self .assertEqual (w .labels_model .get_dict (), dict (v .annotations ))
217205
218206 w .set_data (None )
219207 self .assertEqual (w .name_edit .text (), "" )
220208 self .assertEqual (w .labels_model .get_dict (), {})
221- self .assertIs (w .get_data (), None )
209+ self .assertEqual (w .get_data (), ( None , []) )
222210
223211 def test_discrete_editor (self ):
224212 w = DiscreteVariableEditor ()
225- self .assertIs (w .get_data (), None )
213+ self .assertEqual (w .get_data (), ( None , []) )
226214
227- v = DiscreteVariable ("C" , values = [ "a" , "b" , "c" ])
228- v . attributes . update ({ "A" : 1 , "B" : "b" } )
215+ v = Categorical ("C" , ( "a" , "b" , "c" ), None ,
216+ (( "A" , "1" ), ( "B" , "b" )) )
229217 w .set_data (v )
230218
231219 self .assertEqual (w .name_edit .text (), v .name )
232- self .assertEqual (w .labels_model .get_dict (), v .attributes )
233- self .assertTrue (w .is_same ())
234-
220+ self .assertEqual (w .labels_model .get_dict (), dict (v .annotations ))
221+ self .assertEqual (w .get_data (), (v , []))
235222 w .set_data (None )
236223 self .assertEqual (w .name_edit .text (), "" )
237224 self .assertEqual (w .labels_model .get_dict (), {})
238- self .assertIs (w .get_data (), None )
225+ self .assertEqual (w .get_data (), (None , []))
226+ mapping = [
227+ ("c" , "C" ),
228+ ("a" , "A" ),
229+ ("b" , None ),
230+ (None , "b" )
231+ ]
232+ w .set_data (v , [CategoriesMapping (mapping )])
233+ w .grab () # run delegate paint method
234+ self .assertEqual (w .get_data (), (v , [CategoriesMapping (mapping )]))
239235
240236 # test selection/deselection in the view
241237 w .set_data (v )
@@ -249,21 +245,79 @@ def test_discrete_editor(self):
249245
250246 def test_time_editor (self ):
251247 w = TimeVariableEditor ()
252- self .assertIs (w .get_data (), None )
248+ self .assertEqual (w .get_data (), ( None , []) )
253249
254- v = TimeVariable ("T" , have_date = 1 )
255- v .attributes .update ({"A" : 1 , "B" : "b" })
256- w .set_data (v )
250+ v = Time ("T" , (("A" , "1" ), ("B" , "b" )))
251+ w .set_data (v ,)
257252
258253 self .assertEqual (w .name_edit .text (), v .name )
259- self .assertEqual (w .labels_model .get_dict (), v .attributes )
260- self .assertTrue (w .is_same ())
261-
262- var = w .get_data ()
263- self .assertTrue (var .have_date )
264- self .assertFalse (var .have_time )
254+ self .assertEqual (w .labels_model .get_dict (), dict (v .annotations ))
265255
266256 w .set_data (None )
267257 self .assertEqual (w .name_edit .text (), "" )
268258 self .assertEqual (w .labels_model .get_dict (), {})
269- self .assertIs (w .get_data (), None )
259+ self .assertEqual (w .get_data (), (None , []))
260+
261+
262+ class TestTransforms (TestCase ):
263+ def _test_common (self , var ):
264+ tr = [Rename (var .name + "_copy" ), Annotate ((("A" , "1" ),))]
265+ XX = apply_transform (var , tr )
266+ self .assertEqual (XX .name , var .name + "_copy" )
267+ self .assertEqual (XX .attributes , {"A" : 1 })
268+ self .assertIsInstance (XX .compute_value , Identity )
269+ self .assertIs (XX .compute_value .variable , var )
270+
271+ def test_continous (self ):
272+ X = ContinuousVariable ("X" )
273+ self ._test_common (X )
274+
275+ def test_string (self ):
276+ X = StringVariable ("S" )
277+ self ._test_common (X )
278+
279+ def test_time (self ):
280+ X = TimeVariable ("X" )
281+ self ._test_common (X )
282+
283+ def test_discrete (self ):
284+ D = DiscreteVariable ("D" , values = ("a" , "b" ))
285+ self ._test_common (D )
286+
287+ def test_discrete_rename (self ):
288+ D = DiscreteVariable ("D" , values = ("a" , "b" ))
289+ DD = apply_transform (D , [CategoriesMapping ((("a" , "A" ), ("b" , "B" )))])
290+ self .assertSequenceEqual (DD .values , ["A" , "B" ])
291+ self .assertIs (DD .compute_value .variable , D )
292+
293+ def test_discrete_reorder (self ):
294+ D = DiscreteVariable ("D" , values = ("2" , "3" , "1" , "0" ))
295+ DD = apply_transform (D , [CategoriesMapping ((("0" , "0" ), ("1" , "1" ),
296+ ("2" , "2" ), ("3" , "3" )))])
297+ self .assertSequenceEqual (DD .values , ["0" , "1" , "2" , "3" ])
298+ self ._assertLookupEquals (
299+ DD .compute_value , Lookup (D , np .array ([2 , 3 , 1 , 0 ]))
300+ )
301+
302+ def test_discrete_add_drop (self ):
303+ D = DiscreteVariable ("D" , values = ("2" , "3" , "1" , "0" ), base_value = 1 )
304+ mapping = (
305+ ("0" , None ),
306+ ("1" , "1" ),
307+ ("2" , "2" ),
308+ ("3" , None ),
309+ (None , "A" ),
310+ )
311+ tr = [CategoriesMapping (mapping )]
312+ DD = apply_transform (D , tr )
313+ self .assertSequenceEqual (DD .values , ["1" , "2" , "A" ])
314+ self ._assertLookupEquals (
315+ DD .compute_value , Lookup (D , np .array ([1 , np .nan , 0 , np .nan ]))
316+ )
317+ self .assertEqual (DD .base_value , - 1 )
318+
319+ def _assertLookupEquals (self , first , second ):
320+ self .assertIsInstance (first , Lookup )
321+ self .assertIsInstance (second , Lookup )
322+ self .assertIs (first .variable , second .variable )
323+ assert_array_equal (first .lookup_table , second .lookup_table )
0 commit comments