comparison light9/collector/collector_test.py @ 1506:37cbb245d93c

fix tests. add logging, some mypy types. Ignore-this: 61371f65438a4e77f70d21cc5d5193bf
author Drew Perttula <drewp@bigasterisk.com>
date Tue, 28 Mar 2017 07:48:10 +0000
parents f427801da9f6
children c1bf296b0a74
comparison
equal deleted inserted replaced
1505:e917fb4eea3a 1506:37cbb245d93c
1 import unittest 1 import unittest
2 import datetime 2 import datetime, time
3 from freezegun import freeze_time 3 from freezegun import freeze_time
4 from rdflib import Namespace 4 from rdflib import Namespace, URIRef
5 5
6 from light9.namespaces import L9, DEV 6 from light9.namespaces import L9, DEV
7 from light9.collector.collector import Collector, outputMap 7 from light9.collector.collector import Collector, outputMap
8 from light9.rdfdb.mock_syncedgraph import MockSyncedGraph 8 from light9.rdfdb.mock_syncedgraph import MockSyncedGraph
9 9
15 @prefix dev: <http://light9.bigasterisk.com/device/> . 15 @prefix dev: <http://light9.bigasterisk.com/device/> .
16 @prefix udmx: <http://light9.bigasterisk.com/output/udmx/> . 16 @prefix udmx: <http://light9.bigasterisk.com/output/udmx/> .
17 @prefix dmx0: <http://light9.bigasterisk.com/output/dmx0/> . 17 @prefix dmx0: <http://light9.bigasterisk.com/output/dmx0/> .
18 ''' 18 '''
19 19
20 THEATER = '''
21 :brightness a :DeviceAttr; :dataType :scalar .
22
23 :SimpleDimmer a :DeviceClass;
24 :deviceAttr :brightness;
25 :attr
26 [ :outputAttr :level; :dmxOffset 0 ] .
27
28 :ChauvetColorStrip a :DeviceClass;
29 :deviceAttr :color;
30 :attr
31 [ :outputAttr :mode; :dmxOffset 0 ],
32 [ :outputAttr :red; :dmxOffset 1 ],
33 [ :outputAttr :green; :dmxOffset 2 ],
34 [ :outputAttr :blue; :dmxOffset 3 ] .
35
36 '''
37
38 t0 = 0 # time
39
20 class MockOutput(object): 40 class MockOutput(object):
21 def __init__(self, connections): 41 def __init__(self, uri, connections):
22 self.connections = connections 42 self.connections = connections
23 self.updates = [] 43 self.updates = []
44 self.uri = uri
45 self.numChannels = 4
24 46
25 def allConnections(self): 47 def allConnections(self):
26 return self.connections 48 return self.connections
27 49
28 def update(self, values): 50 def update(self, values):
29 self.updates.append(values) 51 self.updates.append(values)
30 52
31 def flush(self): 53 def flush(self):
32 self.updates.append('flush') 54 self.updates.append('flush')
33 55
56 @unittest.skip("outputMap got rewritten and mostly doesn't raise on these cases")
34 class TestOutputMap(unittest.TestCase): 57 class TestOutputMap(unittest.TestCase):
35 def testWorking(self): 58 def testWorking(self):
36 out0 = MockOutput([(0, DMX0['c1'])]) 59 out0 = MockOutput(UDMX, [(0, DMX0['c1'])])
37 m = outputMap(MockSyncedGraph(PREFIX + ''' 60 m = outputMap(MockSyncedGraph(PREFIX + '''
38 dmx0:c1 :connectedTo dev:inst1Brightness . 61 dmx0:c1 :connectedTo dev:inst1Brightness .
39 dev:inst1 a :Device; :brightness dev:inst1Brightness . 62 dev:inst1 a :Device; :brightness dev:inst1Brightness .
40 '''), [out0]) 63 '''), [out0])
41 self.assertEqual({(DEV['inst1'], L9['brightness']): (out0, 0)}, m) 64 self.assertEqual({(DEV['inst1'], L9['brightness']): (out0, 0)}, m)
65
42 def testMissingOutput(self): 66 def testMissingOutput(self):
43 out0 = MockOutput([(0, DMX0['c1'])]) 67 out0 = MockOutput(UDMX, [(0, DMX0['c1'])])
44 self.assertRaises(KeyError, outputMap, MockSyncedGraph(PREFIX + ''' 68 self.assertRaises(KeyError, outputMap, MockSyncedGraph(PREFIX + '''
45 dmx0:c2 :connectedTo dev:inst1Brightness .
46 dev:inst1 a :Device; :brightness dev:inst1Brightness . 69 dev:inst1 a :Device; :brightness dev:inst1Brightness .
47 '''), [out0]) 70 '''), [out0])
48 71
49 def testMissingOutputConnection(self): 72 def testMissingOutputConnection(self):
50 out0 = MockOutput([(0, DMX0['c1'])]) 73 out0 = MockOutput(UDMX, [(0, DMX0['c1'])])
51 self.assertRaises(ValueError, outputMap, MockSyncedGraph(PREFIX + ''' 74 self.assertRaises(ValueError, outputMap, MockSyncedGraph(PREFIX + '''
52 dev:inst1 a :Device; :brightness dev:inst1Brightness . 75 dev:inst1 a :Device; :brightness dev:inst1Brightness .
53 '''), [out0]) 76 '''), [out0])
54 77
55 def testMultipleOutputConnections(self): 78 def testMultipleOutputConnections(self):
56 out0 = MockOutput([(0, DMX0['c1'])]) 79 out0 = MockOutput(UDMX, [(0, DMX0['c1'])])
57 self.assertRaises(ValueError, outputMap, MockSyncedGraph(PREFIX + ''' 80 self.assertRaises(ValueError, outputMap, MockSyncedGraph(PREFIX + '''
58 dmx0:c1 :connectedTo dev:inst1Brightness . 81 dmx0:c1 :connectedTo dev:inst1Brightness .
59 dmx0:c2 :connectedTo dev:inst1Brightness . 82 dmx0:c2 :connectedTo dev:inst1Brightness .
60 dev:inst1 a :Device; :brightness dev:inst1Brightness . 83 dev:inst1 a :Device; :brightness dev:inst1Brightness .
61 '''), [out0]) 84 '''), [out0])
62 85
63 86
64 87
65 class TestCollector(unittest.TestCase): 88 class TestCollector(unittest.TestCase):
66 def setUp(self): 89 def setUp(self):
67 self.config = MockSyncedGraph(PREFIX + ''' 90 self.config = MockSyncedGraph(PREFIX + THEATER + '''
68
69 udmx:c1 :connectedTo dev:colorStripRed .
70 udmx:c2 :connectedTo dev:colorStripGreen .
71 udmx:c3 :connectedTo dev:colorStripBlue .
72 udmx:c4 :connectedTo dev:colorStripMode .
73 91
74 dev:colorStrip a :Device, :ChauvetColorStrip; 92 dev:colorStrip a :Device, :ChauvetColorStrip;
93 :dmxUniverse udmx:; :dmxBase 1;
75 :red dev:colorStripRed; 94 :red dev:colorStripRed;
76 :green dev:colorStripGreen; 95 :green dev:colorStripGreen;
77 :blue dev:colorStripBlue; 96 :blue dev:colorStripBlue;
78 :mode dev:colorStripMode . 97 :mode dev:colorStripMode .
79 98
80 dmx0:c1 :connectedTo dev:inst1Brightness . 99 dev:inst1 a :Device, :SimpleDimmer;
81 dev:inst1 a :Device, :Dimmer; 100 :dmxUniverse dmx0:; :dmxBase 1;
82 :brightness dev:inst1Brightness . 101 :level dev:inst1Brightness .
83 ''') 102 ''')
84 103
85 self.dmx0 = MockOutput([(0, DMX0['c1'])]) 104 self.dmx0 = MockOutput(DMX0[None], [(0, DMX0['c1'])])
86 self.udmx = MockOutput([(0, UDMX['c1']), 105 self.udmx = MockOutput(UDMX[None], [(0, UDMX['c1']),
87 (1, UDMX['c2']), 106 (1, UDMX['c2']),
88 (2, UDMX['c3']), 107 (2, UDMX['c3']),
89 (3, UDMX['c4'])]) 108 (3, UDMX['c4'])])
90 109
91 def testRoutesColorOutput(self): 110 def testRoutesColorOutput(self):
92 c = Collector(self.config, outputs=[self.dmx0, self.udmx]) 111 c = Collector(self.config, outputs=[self.dmx0, self.udmx])
93 112
94 c.setAttrs('client', 'sess1', 113 c.setAttrs('client', 'sess1',
95 [(DEV['colorStrip'], L9['color'], '#00ff00')]) 114 [(DEV['colorStrip'], L9['color'], '#00ff00')], t0)
96 115
97 self.assertEqual([[0, 255, 0, 215], 'flush'], self.udmx.updates) 116 self.assertEqual([[215, 0, 255, 0], 'flush'], self.udmx.updates)
98 self.assertEqual([], self.dmx0.updates) 117 self.assertEqual([[0, 0, 0, 0], 'flush'], self.dmx0.updates)
99 118
100 def testOutputMaxOfTwoClients(self): 119 def testOutputMaxOfTwoClients(self):
101 c = Collector(self.config, outputs=[self.dmx0, self.udmx]) 120 c = Collector(self.config, outputs=[self.dmx0, self.udmx])
102 121
103 c.setAttrs('client1', 'sess1', 122 c.setAttrs('client1', 'sess1',
104 [(DEV['colorStrip'], L9['color'], '#ff0000')]) 123 [(DEV['colorStrip'], L9['color'], '#ff0000')], t0)
105 c.setAttrs('client2', 'sess1', 124 c.setAttrs('client2', 'sess1',
106 [(DEV['colorStrip'], L9['color'], '#333333')]) 125 [(DEV['colorStrip'], L9['color'], '#333333')], t0)
107 126
108 self.assertEqual([[255, 0, 0, 215], 'flush', 127 self.assertEqual([[215, 255, 0, 0], 'flush',
109 [255, 51, 51, 215], 'flush'], 128 [215, 255, 51, 51], 'flush'],
110 self.udmx.updates) 129 self.udmx.updates)
111 self.assertEqual([], self.dmx0.updates) 130 self.assertEqual([[0, 0, 0, 0], 'flush', [0, 0, 0, 0], 'flush'],
131 self.dmx0.updates)
112 132
113 def testClientOnSameOutputIsRememberedOverCalls(self): 133 def testClientOnSameOutputIsRememberedOverCalls(self):
114 c = Collector(self.config, outputs=[self.dmx0, self.udmx]) 134 c = Collector(self.config, outputs=[self.dmx0, self.udmx])
115 135
116 c.setAttrs('client1', 'sess1', 136 c.setAttrs('client1', 'sess1',
117 [(DEV['colorStrip'], L9['color'], '#080000')]) 137 [(DEV['colorStrip'], L9['color'], '#080000')], t0)
118 c.setAttrs('client2', 'sess1', 138 c.setAttrs('client2', 'sess1',
119 [(DEV['colorStrip'], L9['color'], '#060000')]) 139 [(DEV['colorStrip'], L9['color'], '#060000')], t0)
120 c.setAttrs('client1', 'sess1', 140 c.setAttrs('client1', 'sess1',
121 [(DEV['colorStrip'], L9['color'], '#050000')]) 141 [(DEV['colorStrip'], L9['color'], '#050000')], t0)
122 142
123 self.assertEqual([[8, 0, 0, 215], 'flush', 143 self.assertEqual([[215, 8, 0, 0], 'flush',
124 [8, 0, 0, 215], 'flush', 144 [215, 8, 0, 0], 'flush',
125 [6, 0, 0, 215], 'flush'], 145 [215, 6, 0, 0], 'flush'],
126 self.udmx.updates) 146 self.udmx.updates)
127 self.assertEqual([], self.dmx0.updates) 147 self.assertEqual([[0, 0, 0, 0], 'flush',
148 [0, 0, 0, 0], 'flush',
149 [0, 0, 0, 0], 'flush'],
150 self.dmx0.updates)
128 151
129 def testClientsOnDifferentOutputs(self): 152 def testClientsOnDifferentOutputs(self):
130 c = Collector(self.config, outputs=[self.dmx0, self.udmx]) 153 c = Collector(self.config, outputs=[self.dmx0, self.udmx])
131 154
132 c.setAttrs('client1', 'sess1', [(DEV['colorStrip'], L9['color'], '#aa0000')]) 155 c.setAttrs('client1', 'sess1', [(DEV['colorStrip'], L9['color'], '#aa0000')], t0)
133 c.setAttrs('client2', 'sess1', [(DEV['inst1'], L9['brightness'], .5)]) 156 c.setAttrs('client2', 'sess1', [(DEV['inst1'], L9['brightness'], .5)], t0)
134 157
135 # ok that udmx is flushed twice- it can screen out its own duplicates 158 # ok that udmx is flushed twice- it can screen out its own duplicates
136 self.assertEqual([[170, 0, 0, 215], 'flush', 159 self.assertEqual([[215, 170, 0, 0], 'flush',
137 [170, 0, 0, 215], 'flush'], self.udmx.updates) 160 [215, 170, 0, 0], 'flush'], self.udmx.updates)
138 self.assertEqual([[127], 'flush'], self.dmx0.updates) 161 self.assertEqual([[0, 0, 0, 0], 'flush',
162 [127, 0, 0, 0], 'flush'], self.dmx0.updates)
139 163
140 def testNewSessionReplacesPreviousOutput(self): 164 def testNewSessionReplacesPreviousOutput(self):
141 # ..as opposed to getting max'd with it 165 # ..as opposed to getting max'd with it
142 c = Collector(self.config, outputs=[self.dmx0, self.udmx]) 166 c = Collector(self.config, outputs=[self.dmx0, self.udmx])
143 167
144 c.setAttrs('client1', 'sess1', [(DEV['inst1'], L9['brightness'], .8)]) 168 c.setAttrs('client1', 'sess1', [(DEV['inst1'], L9['brightness'], .8)], t0)
145 c.setAttrs('client1', 'sess2', [(DEV['inst1'], L9['brightness'], .5)]) 169 c.setAttrs('client1', 'sess2', [(DEV['inst1'], L9['brightness'], .5)], t0)
146 170
147 self.assertEqual([[204], 'flush', [127], 'flush'], self.dmx0.updates) 171 self.assertEqual([[204, 0, 0, 0], 'flush',
172 [127, 0, 0, 0], 'flush'], self.dmx0.updates)
148 173
149 def testNewSessionDropsPreviousSettingsOfOtherAttrs(self): 174 def testNewSessionDropsPreviousSettingsOfOtherAttrs(self):
150 175 c = Collector(MockSyncedGraph(PREFIX + THEATER + '''
151 c = Collector(MockSyncedGraph(PREFIX + '''
152
153 udmx:c1 :connectedTo dev:colorStripRed .
154 udmx:c2 :connectedTo dev:colorStripGreen .
155 udmx:c3 :connectedTo dev:colorStripBlue .
156 udmx:c4 :connectedTo dev:colorStripMode .
157 176
158 dev:colorStrip a :Device, :ChauvetColorStrip; 177 dev:colorStrip a :Device, :ChauvetColorStrip;
178 :dmxUniverse udmx:; :dmxBase 1;
159 :red dev:colorStripRed; 179 :red dev:colorStripRed;
160 :green dev:colorStripGreen; 180 :green dev:colorStripGreen;
161 :blue dev:colorStripBlue; 181 :blue dev:colorStripBlue;
162 :mode dev:colorStripMode . 182 :mode dev:colorStripMode .
163 183
164 dmx0:c1 :connectedTo dev:inst1Brightness . 184 dev:inst1 a :Device, :SimpleDimmer;
165 dev:inst1 a :Device, :Dimmer; 185 :dmxUniverse dmx0:; :dmxBase 0;
166 :brightness dev:inst1Brightness . 186 :level dev:inst1Brightness .
167 '''), outputs=[self.dmx0, self.udmx]) 187 '''), outputs=[self.dmx0, self.udmx])
168 188
169 c.setAttrs('client1', 'sess1', 189 c.setAttrs('client1', 'sess1',
170 [(DEV['colorStrip'], L9['color'], '#ff0000')]) 190 [(DEV['colorStrip'], L9['color'], '#ff0000')], t0)
171 c.setAttrs('client1', 'sess2', 191 c.setAttrs('client1', 'sess2',
172 [(DEV['colorStrip'], L9['color'], '#00ff00')]) 192 [(DEV['colorStrip'], L9['color'], '#00ff00')], t0)
173 193
174 self.assertEqual([[255, 0, 0, 215], 'flush', 194 self.assertEqual([[215, 255, 0, 0], 'flush',
175 [0, 255, 0, 215], 'flush'], self.udmx.updates) 195 [215, 0, 255, 0], 'flush'], self.udmx.updates)
176 196
177 def testClientIsForgottenAfterAWhile(self): 197 def testClientIsForgottenAfterAWhile(self):
178 with freeze_time(datetime.datetime.now()) as ft: 198 with freeze_time(datetime.datetime.now()) as ft:
179 c = Collector(self.config, outputs=[self.dmx0, self.udmx]) 199 c = Collector(self.config, outputs=[self.dmx0, self.udmx])
180 c.setAttrs('cli1', 'sess1', [(DEV['inst1'], L9['brightness'], .5)]) 200 c.setAttrs('cli1', 'sess1', [(DEV['inst1'], L9['brightness'], .5)],
201 time.time())
181 ft.tick(delta=datetime.timedelta(seconds=1)) 202 ft.tick(delta=datetime.timedelta(seconds=1))
182 c.setAttrs('cli2', 'sess1', [(DEV['inst1'], L9['brightness'], .2)]) 203 # this max's with cli1's value so we still see .5
204 c.setAttrs('cli2', 'sess1', [(DEV['inst1'], L9['brightness'], .2)],
205 time.time())
183 ft.tick(delta=datetime.timedelta(seconds=9.1)) 206 ft.tick(delta=datetime.timedelta(seconds=9.1))
184 c.setAttrs('cli2', 'sess1', [(DEV['inst1'], L9['brightness'], .4)]) 207 # now cli1 is forgotten, so our value appears
185 self.assertEqual([[127], 'flush', [127], 'flush', [102], 'flush'], 208 c.setAttrs('cli2', 'sess1', [(DEV['inst1'], L9['brightness'], .4)],
209 time.time())
210 self.assertEqual([[127, 0, 0, 0], 'flush',
211 [127, 0, 0, 0], 'flush',
212 [102, 0, 0, 0], 'flush'],
186 self.dmx0.updates) 213 self.dmx0.updates)
187 214
188 def testClientUpdatesAreCollected(self): 215 def testClientUpdatesAreNotMerged(self):
189 # second call to setAttrs doesn't forget the first 216 # second call to setAttrs forgets the first
190 c = Collector(self.config, outputs=[self.dmx0, self.udmx]) 217 c = Collector(self.config, outputs=[self.dmx0, self.udmx])
191 218 t0 = time.time()
192 c.setAttrs('client1', 'sess1', [(DEV['inst1'], L9['brightness'], .5)]) 219 c.setAttrs('client1', 'sess1', [(DEV['inst1'], L9['brightness'], .5)], t0)
193 c.setAttrs('client1', 'sess1', [(DEV['inst1'], L9['brightness'], 1)]) 220 c.setAttrs('client1', 'sess1', [(DEV['inst1'], L9['brightness'], 1)], t0)
194 c.setAttrs('client1', 'sess1', [(DEV['colorStrip'], L9['color'], '#00ff00')]) 221 c.setAttrs('client1', 'sess1', [(DEV['colorStrip'], L9['color'], '#00ff00')], t0)
195 222
196 self.assertEqual([[0, 255, 0, 215], 'flush'], self.udmx.updates) 223 self.assertEqual([[0, 0, 0, 0], 'flush',
197 self.assertEqual([[127], 'flush', [255], 'flush', [255], 'flush'], self.dmx0.updates) 224 [0, 0, 0, 0], 'flush',
225 [215, 0, 255, 0], 'flush'],
226 self.udmx.updates)
227 self.assertEqual([[127, 0, 0, 0], 'flush',
228 [255, 0, 0, 0], 'flush',
229 [0, 0, 0, 0], 'flush'],
230 self.dmx0.updates)
198 231
199 def testRepeatedAttributesInOneRequestGetResolved(self): 232 def testRepeatedAttributesInOneRequestGetResolved(self):
200 c = Collector(self.config, outputs=[self.dmx0, self.udmx]) 233 c = Collector(self.config, outputs=[self.dmx0, self.udmx])
201 234
202 c.setAttrs('client1', 'sess1', [ 235 c.setAttrs('client1', 'sess1', [
203 (DEV['inst1'], L9['brightness'], .5), 236 (DEV['inst1'], L9['brightness'], .5),
204 (DEV['inst1'], L9['brightness'], .3), 237 (DEV['inst1'], L9['brightness'], .3),
205 ]) 238 ], t0)
206 self.assertEqual([[127], 'flush'], self.dmx0.updates) 239 self.assertEqual([[127, 0, 0, 0], 'flush'], self.dmx0.updates)
207 240
208 c.setAttrs('client1', 'sess1', [ 241 c.setAttrs('client1', 'sess1', [
209 (DEV['inst1'], L9['brightness'], .3), 242 (DEV['inst1'], L9['brightness'], .3),
210 (DEV['inst1'], L9['brightness'], .5), 243 (DEV['inst1'], L9['brightness'], .5),
211 ]) 244 ], t0)
212 self.assertEqual([[127], 'flush', [127], 'flush'], self.dmx0.updates) 245 self.assertEqual([[127, 0, 0, 0], 'flush',
213 246 [127, 0, 0, 0], 'flush'], self.dmx0.updates)
247