##// END OF EJS Templates
Add the outline of a few new tests for the custom serialization
Jason Grout -
Show More
@@ -1,140 +1,296 b''
1 var xor = function (a, b) {return !a ^ !b;};
1 var xor = function (a, b) {return !a ^ !b;};
2 var isArray = function (a) {
2 var isArray = function (a) {
3 try {
3 try {
4 return Object.toString.call(a) === "[object Array]" || Object.toString.call(a) === "[object RuntimeArray]";
4 return Object.toString.call(a) === "[object Array]" || Object.toString.call(a) === "[object RuntimeArray]";
5 } catch (e) {
5 } catch (e) {
6 return Array.isArray(a);
6 return Array.isArray(a);
7 }
7 }
8 };
8 };
9 var recursive_compare = function(a, b) {
9 var recursive_compare = function(a, b) {
10 // Recursively compare two objects.
10 // Recursively compare two objects.
11 var same = true;
11 var same = true;
12 same = same && !xor(a instanceof Object || typeof a == 'object', b instanceof Object || typeof b == 'object');
12 same = same && !xor(a instanceof Object || typeof a == 'object', b instanceof Object || typeof b == 'object');
13 same = same && !xor(isArray(a), isArray(b));
13 same = same && !xor(isArray(a), isArray(b));
14
14
15 if (same) {
15 if (same) {
16 if (a instanceof Object) {
16 if (a instanceof Object) {
17 var key;
17 var key;
18 for (key in a) {
18 for (key in a) {
19 if (a.hasOwnProperty(key) && !recursive_compare(a[key], b[key])) {
19 if (a.hasOwnProperty(key) && !recursive_compare(a[key], b[key])) {
20 same = false;
20 same = false;
21 break;
21 break;
22 }
22 }
23 }
23 }
24 for (key in b) {
24 for (key in b) {
25 if (b.hasOwnProperty(key) && !recursive_compare(a[key], b[key])) {
25 if (b.hasOwnProperty(key) && !recursive_compare(a[key], b[key])) {
26 same = false;
26 same = false;
27 break;
27 break;
28 }
28 }
29 }
29 }
30 } else {
30 } else {
31 return a === b;
31 return a === b;
32 }
32 }
33 }
33 }
34
34
35 return same;
35 return same;
36 };
36 };
37
37
38 // Test the widget framework.
38 // Test the widget framework.
39 casper.notebook_test(function () {
39 casper.notebook_test(function () {
40 var index;
40 var index;
41
41
42 index = this.append_cell(
42 index = this.append_cell(
43 'from IPython.html import widgets\n' +
43 ['from IPython.html import widgets',
44 'from IPython.display import display, clear_output\n' +
44 'from IPython.display import display, clear_output',
45 'print("Success")');
45 'print("Success")'].join('\n'));
46 this.execute_cell_then(index);
46 this.execute_cell_then(index);
47
47
48 this.then(function () {
48 this.then(function () {
49 // Test multi-set, single touch code. First create a custom widget.
49 // Test multi-set, single touch code. First create a custom widget.
50 this.thenEvaluate(function() {
50 this.thenEvaluate(function() {
51 var MultiSetView = IPython.DOMWidgetView.extend({
51 var MultiSetView = IPython.DOMWidgetView.extend({
52 render: function(){
52 render: function(){
53 this.model.set('a', 1);
53 this.model.set('a', 1);
54 this.model.set('b', 2);
54 this.model.set('b', 2);
55 this.model.set('c', 3);
55 this.model.set('c', 3);
56 this.touch();
56 this.touch();
57 },
57 },
58 });
58 });
59 IPython.WidgetManager.register_widget_view('MultiSetView', MultiSetView);
59 IPython.WidgetManager.register_widget_view('MultiSetView', MultiSetView);
60 }, {});
60 }, {});
61 });
61 });
62
62
63 // Try creating the multiset widget, verify that sets the values correctly.
63 // Try creating the multiset widget, verify that sets the values correctly.
64 var multiset = {};
64 var multiset = {};
65 multiset.index = this.append_cell(
65 multiset.index = this.append_cell([
66 'from IPython.utils.traitlets import Unicode, CInt\n' +
66 'from IPython.utils.traitlets import Unicode, CInt',
67 'class MultiSetWidget(widgets.Widget):\n' +
67 'class MultiSetWidget(widgets.Widget):',
68 ' _view_name = Unicode("MultiSetView", sync=True)\n' +
68 ' _view_name = Unicode("MultiSetView", sync=True)',
69 ' a = CInt(0, sync=True)\n' +
69 ' a = CInt(0, sync=True)',
70 ' b = CInt(0, sync=True)\n' +
70 ' b = CInt(0, sync=True)',
71 ' c = CInt(0, sync=True)\n' +
71 ' c = CInt(0, sync=True)',
72 ' d = CInt(-1, sync=True)\n' + // See if it sends a full state.
72 ' d = CInt(-1, sync=True)', // See if it sends a full state.
73 ' def set_state(self, sync_data):\n' +
73 ' def set_state(self, sync_data):',
74 ' widgets.Widget.set_state(self, sync_data)\n'+
74 ' widgets.Widget.set_state(self, sync_data)'+
75 ' self.d = len(sync_data)\n' +
75 ' self.d = len(sync_data)',
76 'multiset = MultiSetWidget()\n' +
76 'multiset = MultiSetWidget()',
77 'display(multiset)\n' +
77 'display(multiset)',
78 'print(multiset.model_id)');
78 'print(multiset.model_id)'].join('\n'));
79 this.execute_cell_then(multiset.index, function(index) {
79 this.execute_cell_then(multiset.index, function(index) {
80 multiset.model_id = this.get_output_cell(index).text.trim();
80 multiset.model_id = this.get_output_cell(index).text.trim();
81 });
81 });
82
82
83 this.wait_for_widget(multiset);
83 this.wait_for_widget(multiset);
84
84
85 index = this.append_cell(
85 index = this.append_cell(
86 'print("%d%d%d" % (multiset.a, multiset.b, multiset.c))');
86 'print("%d%d%d" % (multiset.a, multiset.b, multiset.c))');
87 this.execute_cell_then(index, function(index) {
87 this.execute_cell_then(index, function(index) {
88 this.test.assertEquals(this.get_output_cell(index).text.trim(), '123',
88 this.test.assertEquals(this.get_output_cell(index).text.trim(), '123',
89 'Multiple model.set calls and one view.touch update state in back-end.');
89 'Multiple model.set calls and one view.touch update state in back-end.');
90 });
90 });
91
91
92 index = this.append_cell(
92 index = this.append_cell(
93 'print("%d" % (multiset.d))');
93 'print("%d" % (multiset.d))');
94 this.execute_cell_then(index, function(index) {
94 this.execute_cell_then(index, function(index) {
95 this.test.assertEquals(this.get_output_cell(index).text.trim(), '3',
95 this.test.assertEquals(this.get_output_cell(index).text.trim(), '3',
96 'Multiple model.set calls sent a partial state.');
96 'Multiple model.set calls sent a partial state.');
97 });
97 });
98
98
99 var textbox = {};
99 var textbox = {};
100 throttle_index = this.append_cell(
100 throttle_index = this.append_cell([
101 'import time\n' +
101 'import time',
102 'textbox = widgets.Text()\n' +
102 'textbox = widgets.Text()',
103 'display(textbox)\n' +
103 'display(textbox)',
104 'textbox._dom_classes = ["my-throttle-textbox"]\n' +
104 'textbox._dom_classes = ["my-throttle-textbox"]',
105 'def handle_change(name, old, new):\n' +
105 'def handle_change(name, old, new):',
106 ' display(len(new))\n' +
106 ' display(len(new))',
107 ' time.sleep(0.5)\n' +
107 ' time.sleep(0.5)',
108 'textbox.on_trait_change(handle_change, "value")\n' +
108 'textbox.on_trait_change(handle_change, "value")',
109 'print(textbox.model_id)');
109 'print(textbox.model_id)'].join('\n'));
110 this.execute_cell_then(throttle_index, function(index){
110 this.execute_cell_then(throttle_index, function(index){
111 textbox.model_id = this.get_output_cell(index).text.trim();
111 textbox.model_id = this.get_output_cell(index).text.trim();
112
112
113 this.test.assert(this.cell_element_exists(index,
113 this.test.assert(this.cell_element_exists(index,
114 '.widget-area .widget-subarea'),
114 '.widget-area .widget-subarea'),
115 'Widget subarea exists.');
115 'Widget subarea exists.');
116
116
117 this.test.assert(this.cell_element_exists(index,
117 this.test.assert(this.cell_element_exists(index,
118 '.my-throttle-textbox'), 'Textbox exists.');
118 '.my-throttle-textbox'), 'Textbox exists.');
119
119
120 // Send 20 characters
120 // Send 20 characters
121 this.sendKeys('.my-throttle-textbox input', '12345678901234567890');
121 this.sendKeys('.my-throttle-textbox input', '12345678901234567890');
122 });
122 });
123
123
124 this.wait_for_widget(textbox);
124 this.wait_for_widget(textbox);
125
125
126 this.then(function () {
126 this.then(function () {
127 var outputs = this.evaluate(function(i) {
127 var outputs = this.evaluate(function(i) {
128 return IPython.notebook.get_cell(i).output_area.outputs;
128 return IPython.notebook.get_cell(i).output_area.outputs;
129 }, {i : throttle_index});
129 }, {i : throttle_index});
130
130
131 // Only 4 outputs should have printed, but because of timing, sometimes
131 // Only 4 outputs should have printed, but because of timing, sometimes
132 // 5 outputs will print. All we need to do is verify num outputs <= 5
132 // 5 outputs will print. All we need to do is verify num outputs <= 5
133 // because that is much less than 20.
133 // because that is much less than 20.
134 this.test.assert(outputs.length <= 5, 'Messages throttled.');
134 this.test.assert(outputs.length <= 5, 'Messages throttled.');
135
135
136 // We also need to verify that the last state sent was correct.
136 // We also need to verify that the last state sent was correct.
137 var last_state = outputs[outputs.length-1].data['text/plain'];
137 var last_state = outputs[outputs.length-1].data['text/plain'];
138 this.test.assertEquals(last_state, "20", "Last state sent when throttling.");
138 this.test.assertEquals(last_state, "20", "Last state sent when throttling.");
139 });
139 });
140
141
142 /* New Test
143
144
145 %%javascript
146 define('TestWidget', ['widgets/js/widget', 'base/js/utils'], function(widget, utils) {
147 var TestWidget = widget.DOMWidgetView.extend({
148 render: function () {
149 this.listenTo(this.model, 'msg:custom', this.handle_msg);
150 window.w = this;
151 console.log('data:', this.model.get('data'));
152 },
153 handle_msg: function(content, buffers) {
154 this.msg = [content, buffers];
155 }
156 });
157
158 var floatArray = {
159 deserialize: function (value, model) {
160 // DataView -> float64 typed array
161 return new Float64Array(value.buffer);
162 },
163 // serialization automatically handled by message buffers
164 };
165
166
167 var floatList = {
168 deserialize: function (value, model) {
169 // list of floats -> list of strings
170 return value.map(function(x) {return x.toString()});
171 },
172 serialize: function(value, model) {
173 // list of strings -> list of floats
174 return value.map(function(x) {return parseFloat(x);})
175 }
176 };
177 return {TestWidget: TestWidget, floatArray: floatArray, floatList: floatList};
178 });
179
180
181 --------------
182
183
184 from IPython.html import widgets
185 from IPython.utils.traitlets import Unicode, Instance, List
186 from IPython.display import display
187 from array import array
188 def _array_to_memoryview(x):
189 if x is None: return None, {}
190 try:
191 y = memoryview(x)
192 except TypeError:
193 # in python 2, arrays don't support the new buffer protocol
194 y = memoryview(buffer(x))
195 return y, {'serialization': ('floatArray', 'TestWidget')}
196
197 def _memoryview_to_array(x):
198 return array('d', x.tobytes())
199
200 arrays_binary = {
201 'from_json': _memoryview_to_array,
202 'to_json': _array_to_memoryview
203 }
204
205 def _array_to_list(x):
206 if x is None: return None, {}
207 return list(x), {'serialization': ('floatList', 'TestWidget')}
208
209 def _list_to_array(x):
210 return array('d',x)
211
212 arrays_list = {
213 'from_json': _list_to_array,
214 'to_json': _array_to_list
215 }
216
217
218 class TestWidget(widgets.DOMWidget):
219 _view_module = Unicode('TestWidget', sync=True)
220 _view_name = Unicode('TestWidget', sync=True)
221 array_binary = Instance(array, sync=True, **arrays_binary)
222 array_list = Instance(array, sync=True, **arrays_list)
223 def __init__(self, **kwargs):
224 super(widgets.DOMWidget, self).__init__(**kwargs)
225 self.on_msg(self._msg)
226 def _msg(self, _, content, buffers):
227 self.msg = [content, buffers]
228
229
230 ----------------
231
232 x=TestWidget()
233 display(x)
234 x.array_binary=array('d', [1.5,2.5,5])
235 print x.model_id
236
237 -----------------
238
239 %%javascript
240 console.log(w.model.get('array_binary'))
241 var z = w.model.get('array_binary')
242 z[0]*=3
243 z[1]*=3
244 z[2]*=3
245 // we set to null so that we recognize the change
246 // when we set data back to z
247 w.model.set('array_binary', null)
248 w.model.set('array_binary', z)
249 console.log(w.model.get('array_binary'))
250 w.touch()
251
252 ----------------
253 x.array_binary.tolist() == [4.5, 7.5, 15.0]
254 ----------------
255
256 x.array_list = array('d', [1.5, 2.0, 3.1])
257 ----------------
258
259 %%javascript
260 console.log(w.model.get('array_list'))
261 var z = w.model.get('array_list')
262 z[0]+="1234"
263 z[1]+="5678"
264 // we set to null so that we recognize the change
265 // when we set data back to z
266 w.model.set('array_list', null)
267 w.model.set('array_list', z)
268 w.touch()
269
270 -----------------
271
272 x.array_list.tolist() == [1.51234, 25678.0, 3.1]
273
274 -------------------
275 x.send('some content', [memoryview(b'binarycontent'), memoryview('morecontent')])
276
277 -------------------
278
279 %%javascript
280 console.log(w.msg[0] === 'some content')
281 var d=new TextDecoder('utf-8')
282 console.log(d.decode(w.msg[1][0])==='binarycontent')
283 console.log(d.decode(w.msg[1][1])==='morecontent')
284 w.send('content back', [new Uint8Array([1,2,3,4]), new Float64Array([2.1828, 3.14159])])
285
286 --------------------
287
288 print x.msg[0] == 'content back'
289 print x.msg[1][0].tolist() == [1,2,3,4]
290 print array('d', x.msg[1][1].tobytes()).tolist() == [2.1828, 3.14159]
291
292
293 */
294
295
140 });
296 });
General Comments 0
You need to be logged in to leave comments. Login now