@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
4
5 public function testMergeTimeRanges() {
6
7 // Overlapping ranges.
8 $input = array(
9 array(50, 150),
10 array(100, 175),
11 );
12 $expect = array(
13 array(50, 175),
14 );
15
16 $this->assertEqual($expect, PhrequentTimeBlock::mergeTimeRanges($input));
17
18
19 // Identical ranges.
20 $input = array(
21 array(1, 1),
22 array(1, 1),
23 array(1, 1),
24 );
25 $expect = array(
26 array(1, 1),
27 );
28
29 $this->assertEqual($expect, PhrequentTimeBlock::mergeTimeRanges($input));
30
31
32 // Range which is a strict subset of another range.
33 $input = array(
34 array(2, 7),
35 array(1, 10),
36 );
37 $expect = array(
38 array(1, 10),
39 );
40
41 $this->assertEqual($expect, PhrequentTimeBlock::mergeTimeRanges($input));
42
43
44 // These are discontinuous and should not be merged.
45 $input = array(
46 array(5, 6),
47 array(7, 8),
48 );
49 $expect = array(
50 array(5, 6),
51 array(7, 8),
52 );
53
54 $this->assertEqual($expect, PhrequentTimeBlock::mergeTimeRanges($input));
55
56
57 // These overlap only on an edge, but should merge.
58 $input = array(
59 array(5, 6),
60 array(6, 7),
61 );
62 $expect = array(
63 array(5, 7),
64 );
65
66 $this->assertEqual($expect, PhrequentTimeBlock::mergeTimeRanges($input));
67 }
68
69 public function testPreemptingEvents() {
70
71 // Roughly, this is: got into work, started T1, had a meeting from 10-11,
72 // left the office around 2 to meet a client at a coffee shop, worked on
73 // T1 again for about 40 minutes, had the meeting from 3-3:30, finished up
74 // on T1, headed back to the office, got another hour of work in, and
75 // headed home.
76
77 $event = $this->newEvent('T1', 900, 1700);
78
79 $event->attachPreemptingEvents(
80 array(
81 $this->newEvent('meeting', 1000, 1100),
82 $this->newEvent('offsite', 1400, 1600),
83 $this->newEvent('T1', 1420, 1580),
84 $this->newEvent('offsite meeting', 1500, 1550),
85 ));
86
87 $block = new PhrequentTimeBlock(array($event));
88
89 $ranges = $block->getObjectTimeRanges();
90 $ranges = $this->reduceRanges($ranges);
91
92 $this->assertEqual(
93 array(
94 'T1' => array(
95 array(900, 1000), // Before morning meeting.
96 array(1100, 1400), // After morning meeting.
97 array(1420, 1500), // Coffee, before client meeting.
98 array(1550, 1580), // Coffee, after client meeting.
99 array(1600, 1700), // After returning from off site.
100 ),
101 ),
102 $ranges);
103
104 $event = $this->newEvent('T2', 100, 300);
105 $event->attachPreemptingEvents(
106 array(
107 $this->newEvent('meeting', 200, null),
108 ));
109
110 $block = new PhrequentTimeBlock(array($event));
111
112 $ranges = $block->getObjectTimeRanges();
113 $ranges = $this->reduceRanges($ranges);
114
115 $this->assertEqual(
116 array(
117 'T2' => array(
118 array(100, 200),
119 ),
120 ),
121 $ranges);
122 }
123
124 public function testTimelineSort() {
125 $e1 = $this->newEvent('X1', 1, 1)->setID(1);
126
127 $in = array(
128 array(
129 'event' => $e1,
130 'at' => 1,
131 'type' => 'start',
132 ),
133 array(
134 'event' => $e1,
135 'at' => 1,
136 'type' => 'end',
137 ),
138 );
139
140 usort($in, array('PhrequentTimeBlock', 'sortTimeline'));
141
142 $this->assertEqual(
143 array(
144 'start',
145 'end',
146 ),
147 ipull($in, 'type'));
148 }
149
150 public function testInstantaneousEvent() {
151
152 $event = $this->newEvent('T1', 8, 8);
153 $event->attachPreemptingEvents(array());
154
155 $block = new PhrequentTimeBlock(array($event));
156
157 $ranges = $block->getObjectTimeRanges();
158 $ranges = $this->reduceRanges($ranges);
159
160 $this->assertEqual(
161 array(
162 'T1' => array(
163 array(8, 8),
164 ),
165 ),
166 $ranges);
167 }
168
169 public function testPopAcrossStrata() {
170
171 $event = $this->newEvent('T1', 1, 1000);
172 $event->attachPreemptingEvents(
173 array(
174 $this->newEvent('T2', 100, 300),
175 $this->newEvent('T1', 200, 400),
176 $this->newEvent('T3', 250, 275),
177 ));
178
179 $block = new PhrequentTimeBlock(array($event));
180
181 $ranges = $block->getObjectTimeRanges();
182 $ranges = $this->reduceRanges($ranges);
183
184 $this->assertEqual(
185 array(
186 'T1' => array(
187 array(1, 100),
188 array(200, 250),
189 array(275, 1000),
190 ),
191 ),
192 $ranges);
193 }
194
195 public function testEndDeeperStratum() {
196 $event = $this->newEvent('T1', 1, 1000);
197 $event->attachPreemptingEvents(
198 array(
199 $this->newEvent('T2', 100, 900),
200 $this->newEvent('T1', 200, 400),
201 $this->newEvent('T3', 300, 800),
202 $this->newEvent('T1', 350, 600),
203 $this->newEvent('T4', 380, 390),
204 ));
205
206 $block = new PhrequentTimeBlock(array($event));
207
208 $ranges = $block->getObjectTimeRanges();
209 $ranges = $this->reduceRanges($ranges);
210
211 $this->assertEqual(
212 array(
213 'T1' => array(
214 array(1, 100),
215 array(200, 300),
216 array(350, 380),
217 array(390, 600),
218 array(900, 1000),
219 ),
220 ),
221 $ranges);
222 }
223
224 public function testOngoing() {
225 $event = $this->newEvent('T1', 1, null);
226 $event->attachPreemptingEvents(array());
227
228 $block = new PhrequentTimeBlock(array($event));
229
230 $ranges = $block->getObjectTimeRanges();
231 $ranges = $this->reduceRanges($ranges);
232
233 $this->assertEqual(
234 array(
235 'T1' => array(
236 array(1, null),
237 ),
238 ),
239 $ranges);
240 }
241
242 public function testOngoingInterrupted() {
243 $event = $this->newEvent('T1', 1, null);
244 $event->attachPreemptingEvents(
245 array(
246 $this->newEvent('T2', 100, 900),
247 ));
248
249 $block = new PhrequentTimeBlock(array($event));
250
251 $ranges = $block->getObjectTimeRanges();
252 $ranges = $this->reduceRanges($ranges);
253
254 $this->assertEqual(
255 array(
256 'T1' => array(
257 array(1, 100),
258 array(900, null),
259 ),
260 ),
261 $ranges);
262 }
263
264 public function testOngoingPreempted() {
265 $event = $this->newEvent('T1', 1, null);
266 $event->attachPreemptingEvents(
267 array(
268 $this->newEvent('T2', 100, null),
269 ));
270
271 $block = new PhrequentTimeBlock(array($event));
272
273 $ranges = $block->getObjectTimeRanges();
274 $ranges = $this->reduceRanges($ranges);
275
276 $this->assertEqual(
277 array(
278 'T1' => array(
279 array(1, 100),
280 ),
281 ),
282 $ranges);
283 }
284
285 public function testSumTimeSlices() {
286 // This block has multiple closed slices.
287 $block = new PhrequentTimeBlock(
288 array(
289 $this->newEvent('T1', 3456, 4456)->attachPreemptingEvents(array()),
290 $this->newEvent('T1', 8000, 9000)->attachPreemptingEvents(array()),
291 ));
292
293 $this->assertEqual(
294 2000,
295 $block->getTimeSpentOnObject('T1', 10000));
296
297 // This block has an open slice.
298 $block = new PhrequentTimeBlock(
299 array(
300 $this->newEvent('T1', 3456, 4456)->attachPreemptingEvents(array()),
301 $this->newEvent('T1', 8000, null)->attachPreemptingEvents(array()),
302 ));
303
304 $this->assertEqual(
305 3000,
306 $block->getTimeSpentOnObject('T1', 10000));
307 }
308
309 private function newEvent($object_phid, $start_time, $end_time) {
310 static $id = 0;
311
312 return id(new PhrequentUserTime())
313 ->setID(++$id)
314 ->setObjectPHID($object_phid)
315 ->setDateStarted($start_time)
316 ->setDateEnded($end_time);
317 }
318
319 private function reduceRanges(array $ranges) {
320 $results = array();
321
322 foreach ($ranges as $phid => $slices) {
323 $results[$phid] = $slices->getRanges();
324 }
325
326 return $results;
327 }
328
329}