timr 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/timr +2 -1
- data/lib/timr/stack.rb +6 -7
- data/lib/timr/task.rb +23 -19
- data/lib/timr/timr.rb +26 -74
- data/lib/timr/track.rb +25 -25
- data/lib/timr/version.rb +2 -2
- data/lib/timr/window.rb +8 -2
- data/lib/timr/window_help.rb +6 -5
- data/lib/timr/window_tasks.rb +2 -15
- data/lib/timr/window_test.rb +5 -19
- data/lib/timr/window_timeline.rb +2 -13
- data/tests/tc_stack.rb +13 -10
- data/tests/tc_task.rb +48 -23
- data/tests/tc_track.rb +37 -16
- data/tests/tc_window.rb +14 -11
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3b190f08b68a3b332d9e275c9773cd75187d88fa
|
|
4
|
+
data.tar.gz: c6c3941e0d5afd7352e4c4187161e14d5909a3d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f19bb6ba4f42fd18390ecd08030f58a4ac5158cf3067703b02cf8b023e5b5d5ad47fe4f1c076e0641db3485e7753a4273346e39a9103df8364af692d83de1761
|
|
7
|
+
data.tar.gz: 9d8557cb636b962b1062f8f4619a8a86cc7f44eb7cc91ea16e74ec1d877f7c4c1f626121f7f88a287971cb3af68cd74ed5eedce73eb856cdf3aa8147e52fe02a
|
data/bin/timr
CHANGED
data/lib/timr/stack.rb
CHANGED
|
@@ -25,7 +25,11 @@ module TheFox
|
|
|
25
25
|
@tasks.map{ |task|
|
|
26
26
|
status = task.status
|
|
27
27
|
status = '*' if task == @task
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
task_name = task.to_s
|
|
30
|
+
task_name = task.track.name if task.has_track?
|
|
31
|
+
|
|
32
|
+
"#{status} #{task_name}"
|
|
29
33
|
}
|
|
30
34
|
end
|
|
31
35
|
|
|
@@ -43,13 +47,8 @@ module TheFox
|
|
|
43
47
|
|
|
44
48
|
def pop_all(new_task = nil, parent_track = nil)
|
|
45
49
|
if @task == new_task
|
|
46
|
-
|
|
47
|
-
x = @task.start(parent_track)
|
|
48
|
-
#puts "return #{x}"
|
|
49
|
-
#puts
|
|
50
|
-
x
|
|
50
|
+
@task.start(parent_track)
|
|
51
51
|
else
|
|
52
|
-
#puts 'stop all tasks'
|
|
53
52
|
@tasks.each do |task|
|
|
54
53
|
task.stop
|
|
55
54
|
end
|
data/lib/timr/task.rb
CHANGED
|
@@ -76,9 +76,9 @@ module TheFox
|
|
|
76
76
|
when :running
|
|
77
77
|
?>
|
|
78
78
|
when :paused
|
|
79
|
-
?#
|
|
80
|
-
when :stop
|
|
81
79
|
?|
|
|
80
|
+
when :stop
|
|
81
|
+
?.
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
|
|
@@ -92,7 +92,7 @@ module TheFox
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def name
|
|
95
|
-
@meta['name']
|
|
95
|
+
@meta['name'].to_s
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def name=(name)
|
|
@@ -132,13 +132,15 @@ module TheFox
|
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
134
|
elsif paused?
|
|
135
|
-
parent_track = @track
|
|
135
|
+
parent_track = @track if parent_track.nil?
|
|
136
136
|
create_new_track = true
|
|
137
137
|
else
|
|
138
138
|
create_new_track = true
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
if create_new_track
|
|
142
|
+
@changed = true
|
|
143
|
+
|
|
142
144
|
@track = Track.new
|
|
143
145
|
if !parent_track.nil?
|
|
144
146
|
@track.parent = parent_track
|
|
@@ -148,8 +150,6 @@ module TheFox
|
|
|
148
150
|
@track.task = self
|
|
149
151
|
@track.begin_time = Time.now
|
|
150
152
|
@timeline << @track
|
|
151
|
-
|
|
152
|
-
@changed = true
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
@status = :running
|
|
@@ -158,10 +158,11 @@ module TheFox
|
|
|
158
158
|
end
|
|
159
159
|
|
|
160
160
|
def pause
|
|
161
|
-
if !@track.nil?
|
|
161
|
+
if running? && !@track.nil?
|
|
162
162
|
@status = :paused
|
|
163
163
|
@changed = true
|
|
164
164
|
@track.end_time = Time.now
|
|
165
|
+
@timeline_diff_total = nil
|
|
165
166
|
end
|
|
166
167
|
end
|
|
167
168
|
|
|
@@ -181,21 +182,18 @@ module TheFox
|
|
|
181
182
|
|
|
182
183
|
def toggle
|
|
183
184
|
if running?
|
|
184
|
-
|
|
185
|
+
pause
|
|
185
186
|
else
|
|
186
187
|
start
|
|
187
188
|
end
|
|
188
189
|
end
|
|
189
190
|
|
|
190
|
-
def
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
track_description = ": #{@track.description}"
|
|
194
|
-
end
|
|
195
|
-
'%s%s' % [name, track_description]
|
|
191
|
+
def remove_track(track)
|
|
192
|
+
@changed = true
|
|
193
|
+
@timeline.delete(track)
|
|
196
194
|
end
|
|
197
195
|
|
|
198
|
-
def
|
|
196
|
+
def to_s
|
|
199
197
|
name
|
|
200
198
|
end
|
|
201
199
|
|
|
@@ -204,7 +202,7 @@ module TheFox
|
|
|
204
202
|
minutes = 0
|
|
205
203
|
seconds = 0
|
|
206
204
|
|
|
207
|
-
if
|
|
205
|
+
if running? && has_track?
|
|
208
206
|
diff = (end_time - @track.begin_time).to_i.abs
|
|
209
207
|
hours = diff / 3600
|
|
210
208
|
|
|
@@ -222,7 +220,13 @@ module TheFox
|
|
|
222
220
|
# Cache all other tracks.
|
|
223
221
|
if @timeline_diff_total.nil?
|
|
224
222
|
@timeline_diff_total = @timeline
|
|
225
|
-
.select{ |track|
|
|
223
|
+
.select{ |track|
|
|
224
|
+
if running?
|
|
225
|
+
track != @track
|
|
226
|
+
else
|
|
227
|
+
true
|
|
228
|
+
end
|
|
229
|
+
}
|
|
226
230
|
.map{ |track| track.diff }
|
|
227
231
|
.inject(:+)
|
|
228
232
|
end
|
|
@@ -232,8 +236,8 @@ module TheFox
|
|
|
232
236
|
seconds = 0
|
|
233
237
|
|
|
234
238
|
track_diff = 0
|
|
235
|
-
if
|
|
236
|
-
track_diff =
|
|
239
|
+
if running?
|
|
240
|
+
track_diff = @track.diff(end_time)
|
|
237
241
|
end
|
|
238
242
|
|
|
239
243
|
diff = @timeline_diff_total.to_i + track_diff
|
data/lib/timr/timr.rb
CHANGED
|
@@ -44,11 +44,8 @@ module TheFox
|
|
|
44
44
|
@window_help = HelpWindow.new
|
|
45
45
|
@window_test = TestWindow.new
|
|
46
46
|
|
|
47
|
-
@window_tasks = TasksWindow.new
|
|
48
|
-
@
|
|
49
|
-
|
|
50
|
-
@window_timeline = TimelineWindow.new
|
|
51
|
-
@window_timeline.tasks = @tasks
|
|
47
|
+
@window_tasks = TasksWindow.new(@tasks)
|
|
48
|
+
@window_timeline = TimelineWindow.new(@tasks)
|
|
52
49
|
end
|
|
53
50
|
|
|
54
51
|
def config_read(path = @config_path)
|
|
@@ -216,7 +213,7 @@ module TheFox
|
|
|
216
213
|
status_s = @stack.task.status
|
|
217
214
|
|
|
218
215
|
track_begin_time_s = '--:--'
|
|
219
|
-
if @stack.task.has_track?
|
|
216
|
+
if @stack.task.running? && @stack.task.has_track?
|
|
220
217
|
track_begin_time_s = @stack.task.track.begin_time.strftime('%R')
|
|
221
218
|
end
|
|
222
219
|
|
|
@@ -298,34 +295,13 @@ module TheFox
|
|
|
298
295
|
end
|
|
299
296
|
end
|
|
300
297
|
|
|
301
|
-
def ui_window_refresh(lines)
|
|
302
|
-
ui_window_refresh_all
|
|
303
|
-
|
|
304
|
-
# if !@window.nil?
|
|
305
|
-
# simple_refresh = @window.cursor_on_inner_range?
|
|
306
|
-
|
|
307
|
-
# if !simple_refresh || simple_refresh != @ui_window_refresh_last
|
|
308
|
-
# ui_window_refresh_all
|
|
309
|
-
# else
|
|
310
|
-
# ui_window_refresh_simple(lines)
|
|
311
|
-
# end
|
|
312
|
-
|
|
313
|
-
# @ui_window_refresh_last = simple_refresh
|
|
314
|
-
# end
|
|
315
|
-
end
|
|
316
|
-
|
|
317
298
|
def ui_window_refresh_all
|
|
318
299
|
if !@window.nil?
|
|
319
300
|
content_line_nr = 1
|
|
320
301
|
@window.content_refresh
|
|
321
302
|
max_line_len = Curses.cols - 2
|
|
322
303
|
@window.page.each do |line_object|
|
|
323
|
-
line_text =
|
|
324
|
-
if line_object.is_a?(Task) || line_object.is_a?(Track)
|
|
325
|
-
line_text = line_object.to_list_s
|
|
326
|
-
else
|
|
327
|
-
line_text = line_object.to_s
|
|
328
|
-
end
|
|
304
|
+
line_text = line_object.to_s
|
|
329
305
|
|
|
330
306
|
if line_text.length > max_line_len
|
|
331
307
|
range = 0..-(line_text.length - max_line_len + 4)
|
|
@@ -361,46 +337,6 @@ module TheFox
|
|
|
361
337
|
Curses.refresh
|
|
362
338
|
end
|
|
363
339
|
|
|
364
|
-
def ui_window_refresh_simple(lines)
|
|
365
|
-
max_line_len = Curses.cols - 2
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
old_cursor = @window.cursor + (lines * -1)
|
|
369
|
-
old_line_object = @window.page[old_cursor - 1]
|
|
370
|
-
|
|
371
|
-
old_line_text = ''
|
|
372
|
-
if old_line_object.is_a?(Task) || old_line_object.is_a?(Track)
|
|
373
|
-
old_line_text = old_line_object.to_list_s
|
|
374
|
-
else
|
|
375
|
-
old_line_text = old_line_object.to_s
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
if old_line_text.length > max_line_len
|
|
379
|
-
range = 0..-(line_text.length - max_line_len + 4)
|
|
380
|
-
old_line_text = "#{old_line_text[range]}..."
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
new_cursor = @window.cursor
|
|
385
|
-
new_line_object = @window.page[new_cursor - 1]
|
|
386
|
-
|
|
387
|
-
new_line_text = ''
|
|
388
|
-
if new_line_object.is_a?(Task) || new_line_object.is_a?(Track)
|
|
389
|
-
new_line_text = new_line_object.to_list_s
|
|
390
|
-
else
|
|
391
|
-
new_line_text = new_line_object.to_s
|
|
392
|
-
end
|
|
393
|
-
|
|
394
|
-
if new_line_text.length > max_line_len
|
|
395
|
-
range = 0..-(line_text.length - max_line_len + 4)
|
|
396
|
-
new_line_text = "#{new_line_text[range]}..."
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
ui_content_line(old_cursor, old_line_text)
|
|
400
|
-
ui_content_line(@window.cursor, new_line_text)
|
|
401
|
-
Curses.refresh
|
|
402
|
-
end
|
|
403
|
-
|
|
404
340
|
def ui_content_length
|
|
405
341
|
Curses.lines - RESERVED_LINES - @stack.size
|
|
406
342
|
end
|
|
@@ -559,7 +495,7 @@ module TheFox
|
|
|
559
495
|
# ui_status_text('DEBUG: %03d C=%03d L=%03d pr=%03d cr=%03d %02d %02d %s %s' % [@window.cursor, ui_content_length, @window.current_line, @window.page_refreshes, @window.content_refreshes, @window.cursor_border_top, @window.cursor_border_bottom, simple_refresh_b ? 'SIMPLE' : 'FULL', simple_refresh_a ? 'SIMPLE' : 'FULL'])
|
|
560
496
|
#end
|
|
561
497
|
|
|
562
|
-
|
|
498
|
+
ui_window_refresh_all
|
|
563
499
|
end
|
|
564
500
|
when Curses::Key::UP
|
|
565
501
|
if !@window.nil? && @window.has_cursor?
|
|
@@ -573,7 +509,7 @@ module TheFox
|
|
|
573
509
|
# ui_status_text('DEBUG: %03d C=%03d L=%03d pr=%03d cr=%03d %02d %02d %s %s' % [@window.cursor, ui_content_length, @window.current_line, @window.page_refreshes, @window.content_refreshes, @window.cursor_border_top, @window.cursor_border_bottom, simple_refresh_b ? 'SIMPLE' : 'FULL', simple_refresh_a ? 'SIMPLE' : 'FULL'])
|
|
574
510
|
# end
|
|
575
511
|
|
|
576
|
-
|
|
512
|
+
ui_window_refresh_all
|
|
577
513
|
end
|
|
578
514
|
when Curses::Key::HOME
|
|
579
515
|
@window.first_page if !@window.nil?
|
|
@@ -591,6 +527,17 @@ module TheFox
|
|
|
591
527
|
# end
|
|
592
528
|
|
|
593
529
|
ui_window_refresh_all
|
|
530
|
+
when Curses::Key::DC
|
|
531
|
+
task, track = window_page_object
|
|
532
|
+
|
|
533
|
+
if track.nil?
|
|
534
|
+
# Remove Task
|
|
535
|
+
else
|
|
536
|
+
# Remove Track
|
|
537
|
+
end
|
|
538
|
+
task.remove_track(track)
|
|
539
|
+
|
|
540
|
+
ui_status_text("Del '#{task.class}' '#{track.class}'")
|
|
594
541
|
when Curses::Key::RESIZE
|
|
595
542
|
update_content_length
|
|
596
543
|
ui_status_text("Window size: #{Curses.cols}x#{Curses.lines}")
|
|
@@ -601,19 +548,24 @@ module TheFox
|
|
|
601
548
|
ui_refresh_all
|
|
602
549
|
when 10
|
|
603
550
|
task, track = window_page_object
|
|
551
|
+
|
|
552
|
+
# if track.nil?
|
|
553
|
+
# track = Track.new
|
|
554
|
+
# track.task = task
|
|
555
|
+
# end
|
|
556
|
+
|
|
604
557
|
task_apply_replace_stack(task, track)
|
|
605
558
|
when '#'
|
|
606
559
|
task, * = window_page_object
|
|
607
560
|
|
|
608
561
|
track_description = ui_status_input('Track Description: ')
|
|
609
|
-
ui_status_text("Description: '#{track_description}'")
|
|
610
562
|
|
|
611
563
|
track = Track.new
|
|
612
564
|
track.task = task
|
|
613
565
|
track.description = track_description
|
|
614
566
|
|
|
615
567
|
task_apply_replace_stack(task, track)
|
|
616
|
-
|
|
568
|
+
ui_status_text('OK')
|
|
617
569
|
when 'b', 'p'
|
|
618
570
|
task, track = window_page_object
|
|
619
571
|
|
|
@@ -657,7 +609,7 @@ module TheFox
|
|
|
657
609
|
ui_status_text("Task '#{task_name}' created.")
|
|
658
610
|
end
|
|
659
611
|
when 'x'
|
|
660
|
-
@stack.task.
|
|
612
|
+
@stack.task.pause if @stack.has_task?
|
|
661
613
|
window_content_changed
|
|
662
614
|
ui_refresh_simple
|
|
663
615
|
when 'c'
|
|
@@ -674,7 +626,7 @@ module TheFox
|
|
|
674
626
|
ui_window_show(@window_timeline)
|
|
675
627
|
when '2'
|
|
676
628
|
ui_window_show(@window_tasks)
|
|
677
|
-
when '
|
|
629
|
+
when 'z' # Test Windows
|
|
678
630
|
ui_window_show(@window_test)
|
|
679
631
|
when 'w'
|
|
680
632
|
tasks_save(true)
|
data/lib/timr/track.rb
CHANGED
|
@@ -76,22 +76,38 @@ module TheFox
|
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
def description
|
|
80
|
-
@description
|
|
79
|
+
def description=(description)
|
|
80
|
+
@description = description == '' ? nil : description
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
def description
|
|
84
|
-
@description
|
|
83
|
+
def description
|
|
84
|
+
@description
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
def diff
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
def diff(end_time = nil)
|
|
88
|
+
end_time = @end_time if !@end_time.nil?
|
|
89
|
+
|
|
90
|
+
if !@begin_time.nil? && !end_time.nil?
|
|
91
|
+
(end_time - @begin_time).abs.to_i
|
|
90
92
|
else
|
|
91
93
|
0
|
|
92
94
|
end
|
|
93
95
|
end
|
|
94
96
|
|
|
97
|
+
def name
|
|
98
|
+
name_s = ''
|
|
99
|
+
if !@task.nil?
|
|
100
|
+
name_s += @task.to_s
|
|
101
|
+
end
|
|
102
|
+
if !@description.nil? && @description.length > 0
|
|
103
|
+
if name_s.length > 0
|
|
104
|
+
name_s += ': '
|
|
105
|
+
end
|
|
106
|
+
name_s += @description
|
|
107
|
+
end
|
|
108
|
+
name_s
|
|
109
|
+
end
|
|
110
|
+
|
|
95
111
|
def to_h
|
|
96
112
|
h = {
|
|
97
113
|
'id' => @id,
|
|
@@ -114,10 +130,6 @@ module TheFox
|
|
|
114
130
|
end
|
|
115
131
|
|
|
116
132
|
def to_s
|
|
117
|
-
@id
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def to_list_s
|
|
121
133
|
end_date = nil
|
|
122
134
|
end_time_s = 'xx:xx'
|
|
123
135
|
if !@end_time.nil?
|
|
@@ -133,20 +145,8 @@ module TheFox
|
|
|
133
145
|
end_date_s = @end_time.localtime.strftime('%F') if !@end_time.nil?
|
|
134
146
|
end
|
|
135
147
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
task_name = @task.to_list_s
|
|
139
|
-
end
|
|
140
|
-
#task_name = @id[0..4]
|
|
141
|
-
|
|
142
|
-
descr_s = ''
|
|
143
|
-
if !@description.nil? && @description.length > 0
|
|
144
|
-
descr_s = ": #{@description}"
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
'%10s %5s - %5s %10s %s%s' % [
|
|
148
|
-
begin_date_s, @begin_time.localtime.strftime('%R'), end_time_s, end_date_s,
|
|
149
|
-
task_name, descr_s]
|
|
148
|
+
'%10s %5s - %5s %10s %s' % [
|
|
149
|
+
begin_date_s, @begin_time.localtime.strftime('%R'), end_time_s, end_date_s, name]
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
def self.from_h(task = nil, h)
|
data/lib/timr/version.rb
CHANGED
data/lib/timr/window.rb
CHANGED
|
@@ -4,7 +4,8 @@ module TheFox
|
|
|
4
4
|
|
|
5
5
|
class Window
|
|
6
6
|
|
|
7
|
-
def initialize
|
|
7
|
+
def initialize(data = [])
|
|
8
|
+
@data = data
|
|
8
9
|
@content_length = 0
|
|
9
10
|
@current_line = 0
|
|
10
11
|
@cursor = 1
|
|
@@ -30,8 +31,13 @@ module TheFox
|
|
|
30
31
|
@content_length = content_length
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
def content=(data)
|
|
35
|
+
@content_changed = true
|
|
36
|
+
@data = data
|
|
37
|
+
end
|
|
38
|
+
|
|
33
39
|
def content
|
|
34
|
-
|
|
40
|
+
@data
|
|
35
41
|
end
|
|
36
42
|
|
|
37
43
|
def content_changed
|
data/lib/timr/window_help.rb
CHANGED
|
@@ -4,14 +4,14 @@ module TheFox
|
|
|
4
4
|
|
|
5
5
|
class HelpWindow < Window
|
|
6
6
|
|
|
7
|
-
def
|
|
8
|
-
[
|
|
7
|
+
def setup
|
|
8
|
+
@data = [
|
|
9
9
|
'#### Help ####',
|
|
10
10
|
'',
|
|
11
11
|
' n .. Create a new Task and start',
|
|
12
12
|
' t .. Create a new Task',
|
|
13
|
-
' c .. Current Task: Start/Continue',
|
|
14
|
-
' x .. Current Task:
|
|
13
|
+
' c .. Current Task: Toggle Start/Continue',
|
|
14
|
+
' x .. Current Task: Pause',
|
|
15
15
|
' v .. Current Task: Stop and Pop from Stack',
|
|
16
16
|
' f .. Stop and deselect all Tasks on the Stack',
|
|
17
17
|
' p, b .. Push and start selected Task.',
|
|
@@ -29,7 +29,8 @@ module TheFox
|
|
|
29
29
|
'Current Task Status',
|
|
30
30
|
'',
|
|
31
31
|
" #{TASK_NO_TASK_LOADED_CHAR} .. No Task loaded.",
|
|
32
|
-
'
|
|
32
|
+
' . .. Task is stopped.',
|
|
33
|
+
' | .. Task is paused.',
|
|
33
34
|
' > .. Task is running.',
|
|
34
35
|
]
|
|
35
36
|
end
|
data/lib/timr/window_tasks.rb
CHANGED
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
|
|
2
|
-
require 'pp'
|
|
3
|
-
|
|
4
2
|
module TheFox
|
|
5
3
|
module Timr
|
|
6
4
|
|
|
7
5
|
class TasksWindow < Window
|
|
8
6
|
|
|
9
|
-
@tasks = nil
|
|
10
|
-
|
|
11
|
-
def setup
|
|
12
|
-
@tasks = nil
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def tasks=(tasks)
|
|
16
|
-
content_changed
|
|
17
|
-
@tasks = tasks
|
|
18
|
-
end
|
|
19
|
-
|
|
20
7
|
def content
|
|
21
|
-
if @
|
|
8
|
+
if @data.nil? || @data.length == 0
|
|
22
9
|
@has_cursor = false
|
|
23
10
|
[
|
|
24
11
|
'',
|
|
@@ -29,7 +16,7 @@ module TheFox
|
|
|
29
16
|
]
|
|
30
17
|
else
|
|
31
18
|
@has_cursor = true
|
|
32
|
-
@
|
|
19
|
+
@data
|
|
33
20
|
.sort_by{ |k, v|
|
|
34
21
|
v.name.downcase
|
|
35
22
|
}
|
data/lib/timr/window_test.rb
CHANGED
|
@@ -4,28 +4,14 @@ module TheFox
|
|
|
4
4
|
|
|
5
5
|
class TestWindow < Window
|
|
6
6
|
|
|
7
|
-
@lines = nil
|
|
8
|
-
|
|
9
7
|
def setup
|
|
10
8
|
@has_cursor = true
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
content_changed
|
|
16
|
-
@lines = lines
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def content
|
|
20
|
-
if @lines.nil?
|
|
21
|
-
c = []
|
|
22
|
-
(1..30).each do |n|
|
|
23
|
-
c << 'LINE %03d 123456789_123456789_123456789_123456789_123456789_123456789' % [n]
|
|
24
|
-
end
|
|
25
|
-
c
|
|
26
|
-
else
|
|
27
|
-
@lines
|
|
9
|
+
|
|
10
|
+
@data = []
|
|
11
|
+
(1..30).each do |n|
|
|
12
|
+
@data << 'LINE %03d 123456789_123456789_123456789_123456789_123456789_123456789' % [n]
|
|
28
13
|
end
|
|
14
|
+
@data
|
|
29
15
|
end
|
|
30
16
|
|
|
31
17
|
end
|
data/lib/timr/window_timeline.rb
CHANGED
|
@@ -4,19 +4,8 @@ module TheFox
|
|
|
4
4
|
|
|
5
5
|
class TimelineWindow < Window
|
|
6
6
|
|
|
7
|
-
@tasks = nil
|
|
8
|
-
|
|
9
|
-
def setup
|
|
10
|
-
@tasks = nil
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def tasks=(tasks)
|
|
14
|
-
content_changed
|
|
15
|
-
@tasks = tasks
|
|
16
|
-
end
|
|
17
|
-
|
|
18
7
|
def content
|
|
19
|
-
if @
|
|
8
|
+
if @data.nil? || @data.length == 0
|
|
20
9
|
@has_cursor = false
|
|
21
10
|
[
|
|
22
11
|
'',
|
|
@@ -27,7 +16,7 @@ module TheFox
|
|
|
27
16
|
]
|
|
28
17
|
else
|
|
29
18
|
@has_cursor = true
|
|
30
|
-
@
|
|
19
|
+
@data
|
|
31
20
|
.map{ |task_id, task|
|
|
32
21
|
task.timeline
|
|
33
22
|
}
|
data/tests/tc_stack.rb
CHANGED
|
@@ -5,24 +5,27 @@ require 'timr'
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class TestStack < MiniTest::Test
|
|
8
|
+
|
|
9
|
+
include TheFox::Timr
|
|
10
|
+
|
|
8
11
|
def test_class_name
|
|
9
|
-
stack1 =
|
|
12
|
+
stack1 = Stack.new
|
|
10
13
|
|
|
11
14
|
assert_equal('TheFox::Timr::Stack', stack1.class.to_s)
|
|
12
15
|
end
|
|
13
16
|
|
|
14
17
|
def test_has_task
|
|
15
|
-
stack1 =
|
|
18
|
+
stack1 = Stack.new
|
|
16
19
|
assert_equal(false, stack1.has_task?)
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
def test_push_pop
|
|
20
|
-
task1 =
|
|
23
|
+
task1 = Task.new
|
|
21
24
|
task1.name = 'task1'
|
|
22
|
-
task2 =
|
|
25
|
+
task2 = Task.new
|
|
23
26
|
task2.name = 'task2'
|
|
24
27
|
|
|
25
|
-
stack1 =
|
|
28
|
+
stack1 = Stack.new
|
|
26
29
|
assert_equal([], stack1.tasks_texts)
|
|
27
30
|
assert_equal(nil, stack1.task)
|
|
28
31
|
assert_equal(0, stack1.size)
|
|
@@ -40,7 +43,7 @@ class TestStack < MiniTest::Test
|
|
|
40
43
|
assert_equal(2, stack1.size)
|
|
41
44
|
assert_equal(true, task2.running?)
|
|
42
45
|
assert_equal(false, task1.running?)
|
|
43
|
-
assert_equal(['
|
|
46
|
+
assert_equal(['| task1', '* task2'], stack1.tasks_texts)
|
|
44
47
|
|
|
45
48
|
# if !@tasks.include?(task)
|
|
46
49
|
push_res = stack1.push(task2)
|
|
@@ -67,14 +70,14 @@ class TestStack < MiniTest::Test
|
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
def test_pop_all
|
|
70
|
-
task3 =
|
|
73
|
+
task3 = Task.new
|
|
71
74
|
#task3.name = 'task3'
|
|
72
|
-
task4 =
|
|
75
|
+
task4 = Task.new
|
|
73
76
|
#task4.name = 'task4'
|
|
74
|
-
task5 =
|
|
77
|
+
task5 = Task.new
|
|
75
78
|
#task5.name = 'task5'
|
|
76
79
|
|
|
77
|
-
stack1 =
|
|
80
|
+
stack1 = Stack.new
|
|
78
81
|
|
|
79
82
|
stack1.push(task3)
|
|
80
83
|
assert_equal(true, task3.running?)
|
data/tests/tc_task.rb
CHANGED
|
@@ -7,14 +7,17 @@ require 'timr'
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TestTask < MiniTest::Test
|
|
10
|
+
|
|
11
|
+
include TheFox::Timr
|
|
12
|
+
|
|
10
13
|
def test_class_name
|
|
11
|
-
task1 =
|
|
14
|
+
task1 = Task.new
|
|
12
15
|
|
|
13
16
|
assert_equal('TheFox::Timr::Task', task1.class.to_s)
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def test_save_load
|
|
17
|
-
task1 =
|
|
20
|
+
task1 = Task.new
|
|
18
21
|
|
|
19
22
|
file_path = task1.save_to_file('tmp')
|
|
20
23
|
assert_equal(false, File.exist?(file_path))
|
|
@@ -24,7 +27,7 @@ class TestTask < MiniTest::Test
|
|
|
24
27
|
file_path = task1.save_to_file('tmp')
|
|
25
28
|
assert_equal(true, File.exist?(file_path))
|
|
26
29
|
|
|
27
|
-
task2 =
|
|
30
|
+
task2 = Task.new
|
|
28
31
|
task2.load_from_file(file_path)
|
|
29
32
|
|
|
30
33
|
assert_equal(task1.id, task2.id)
|
|
@@ -35,9 +38,9 @@ class TestTask < MiniTest::Test
|
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
def test_status
|
|
38
|
-
task1 =
|
|
41
|
+
task1 = Task.new
|
|
39
42
|
assert_equal(false, task1.running?)
|
|
40
|
-
assert_equal(
|
|
43
|
+
assert_equal(?., task1.status)
|
|
41
44
|
|
|
42
45
|
task1.start
|
|
43
46
|
assert_equal(true, task1.running?)
|
|
@@ -45,7 +48,7 @@ class TestTask < MiniTest::Test
|
|
|
45
48
|
|
|
46
49
|
task1.stop
|
|
47
50
|
assert_equal(false, task1.running?)
|
|
48
|
-
assert_equal(
|
|
51
|
+
assert_equal(?., task1.status)
|
|
49
52
|
|
|
50
53
|
task1.start
|
|
51
54
|
assert_equal(true, task1.running?)
|
|
@@ -54,15 +57,15 @@ class TestTask < MiniTest::Test
|
|
|
54
57
|
task1.pause
|
|
55
58
|
assert_equal(false, task1.running?)
|
|
56
59
|
assert_equal(true, task1.paused?)
|
|
57
|
-
assert_equal(
|
|
60
|
+
assert_equal(?|, task1.status)
|
|
58
61
|
|
|
59
62
|
task1.stop
|
|
60
63
|
assert_equal(false, task1.running?)
|
|
61
|
-
assert_equal(
|
|
64
|
+
assert_equal(?., task1.status)
|
|
62
65
|
end
|
|
63
66
|
|
|
64
67
|
def test_start_stop
|
|
65
|
-
task1 =
|
|
68
|
+
task1 = Task.new
|
|
66
69
|
assert_equal(false, task1.running?)
|
|
67
70
|
assert_equal(false, task1.has_track?)
|
|
68
71
|
assert_equal(0, task1.timeline.length)
|
|
@@ -99,15 +102,15 @@ class TestTask < MiniTest::Test
|
|
|
99
102
|
|
|
100
103
|
task1.toggle
|
|
101
104
|
assert_equal(false, task1.running?)
|
|
102
|
-
assert_equal(
|
|
105
|
+
assert_equal(true, task1.has_track?)
|
|
103
106
|
assert_equal(3, task1.timeline.length)
|
|
104
107
|
|
|
105
108
|
# Track
|
|
106
109
|
assert_equal(true, task1.start)
|
|
107
110
|
|
|
108
|
-
track1 =
|
|
111
|
+
track1 = Track.new
|
|
109
112
|
track1.description = 'hello world1'
|
|
110
|
-
track2 =
|
|
113
|
+
track2 = Track.new
|
|
111
114
|
track2.description = 'hello world2'
|
|
112
115
|
|
|
113
116
|
assert_equal(true, task1.start(track1))
|
|
@@ -115,25 +118,47 @@ class TestTask < MiniTest::Test
|
|
|
115
118
|
assert_equal(true, task1.start(track2))
|
|
116
119
|
end
|
|
117
120
|
|
|
121
|
+
def test_remove_track
|
|
122
|
+
task1 = Task.new
|
|
123
|
+
|
|
124
|
+
task1.start
|
|
125
|
+
#track1 = task1.track
|
|
126
|
+
task1.stop
|
|
127
|
+
assert_equal(1, task1.timeline.length)
|
|
128
|
+
|
|
129
|
+
task1.start
|
|
130
|
+
track2 = task1.track
|
|
131
|
+
task1.stop
|
|
132
|
+
assert_equal(2, task1.timeline.length)
|
|
133
|
+
|
|
134
|
+
task1.start
|
|
135
|
+
#track3 = task1.track
|
|
136
|
+
task1.stop
|
|
137
|
+
assert_equal(3, task1.timeline.length)
|
|
138
|
+
|
|
139
|
+
task1.remove_track(track2)
|
|
140
|
+
assert_equal(2, task1.timeline.length)
|
|
141
|
+
end
|
|
142
|
+
|
|
118
143
|
def test_to_s
|
|
119
|
-
task1 =
|
|
144
|
+
task1 = Task.new
|
|
120
145
|
assert_equal('', task1.to_s)
|
|
121
146
|
|
|
122
147
|
task1.name = 'task1'
|
|
123
148
|
assert_equal('task1', task1.to_s)
|
|
124
149
|
|
|
125
|
-
track1 =
|
|
150
|
+
track1 = Track.new
|
|
126
151
|
track1.description = 'hello world1'
|
|
127
152
|
|
|
128
153
|
task1.start(track1)
|
|
129
|
-
assert_equal('task1
|
|
154
|
+
assert_equal('task1', task1.to_s)
|
|
130
155
|
|
|
131
156
|
task1.stop
|
|
132
157
|
assert_equal('task1', task1.to_s)
|
|
133
158
|
end
|
|
134
159
|
|
|
135
160
|
def test_run_time_track
|
|
136
|
-
task1 =
|
|
161
|
+
task1 = Task.new
|
|
137
162
|
|
|
138
163
|
task1.start
|
|
139
164
|
task1.track.begin_time = Time.parse('1987-02-21 09:58:59')
|
|
@@ -141,13 +166,13 @@ class TestTask < MiniTest::Test
|
|
|
141
166
|
end
|
|
142
167
|
|
|
143
168
|
def test_run_time_total
|
|
144
|
-
task1 =
|
|
145
|
-
task1.toggle
|
|
146
|
-
task1.toggle
|
|
147
|
-
task1.toggle
|
|
148
|
-
task1.toggle
|
|
149
|
-
task1.toggle
|
|
150
|
-
task1.toggle
|
|
169
|
+
task1 = Task.new
|
|
170
|
+
task1.toggle # running
|
|
171
|
+
task1.toggle # paused
|
|
172
|
+
task1.toggle # running
|
|
173
|
+
task1.toggle # paused
|
|
174
|
+
task1.toggle # running
|
|
175
|
+
task1.toggle # paused
|
|
151
176
|
|
|
152
177
|
timeline = task1.timeline
|
|
153
178
|
timeline[0].begin_time = Time.parse('1986-11-20 01:01:01')
|
data/tests/tc_track.rb
CHANGED
|
@@ -7,25 +7,28 @@ require 'timr'
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TestTrack < MiniTest::Test
|
|
10
|
+
|
|
11
|
+
include TheFox::Timr
|
|
12
|
+
|
|
10
13
|
def test_class_name
|
|
11
|
-
track1 =
|
|
14
|
+
track1 = Track.new
|
|
12
15
|
|
|
13
16
|
assert_equal('TheFox::Timr::Track', track1.class.to_s)
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def test_basic
|
|
17
|
-
track1 =
|
|
20
|
+
track1 = Track.new
|
|
18
21
|
assert_equal(nil, track1.begin_time)
|
|
19
22
|
assert_equal(nil, track1.end_time)
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
def test_description
|
|
23
|
-
track1 =
|
|
26
|
+
track1 = Track.new
|
|
24
27
|
assert_equal(nil, track1.description)
|
|
25
28
|
|
|
26
29
|
track1.description = 'hello world1'
|
|
27
30
|
|
|
28
|
-
track2 =
|
|
31
|
+
track2 = Track.new
|
|
29
32
|
assert_equal(nil, track2.description)
|
|
30
33
|
|
|
31
34
|
track2.parent = track1
|
|
@@ -36,7 +39,7 @@ class TestTrack < MiniTest::Test
|
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
def test_diff
|
|
39
|
-
track1 =
|
|
42
|
+
track1 = Track.new
|
|
40
43
|
assert_equal(0, track1.diff)
|
|
41
44
|
|
|
42
45
|
track1.begin_time = Time.parse('1986-04-08 13:37:02')
|
|
@@ -50,8 +53,25 @@ class TestTrack < MiniTest::Test
|
|
|
50
53
|
assert_equal(Fixnum, track1.diff.class)
|
|
51
54
|
end
|
|
52
55
|
|
|
56
|
+
def test_name
|
|
57
|
+
task1 = Task.new
|
|
58
|
+
task1.name = 'task1'
|
|
59
|
+
|
|
60
|
+
track1 = Track.new
|
|
61
|
+
track1.description = 'hello world1'
|
|
62
|
+
|
|
63
|
+
task1.start(track1)
|
|
64
|
+
assert_equal('hello world1', track1.name)
|
|
65
|
+
|
|
66
|
+
track1.task = task1
|
|
67
|
+
assert_equal('task1: hello world1', track1.name)
|
|
68
|
+
|
|
69
|
+
task1.stop
|
|
70
|
+
assert_equal('task1: hello world1', track1.name)
|
|
71
|
+
end
|
|
72
|
+
|
|
53
73
|
def test_to_h
|
|
54
|
-
track1 =
|
|
74
|
+
track1 = Track.new
|
|
55
75
|
|
|
56
76
|
h = track1.to_h
|
|
57
77
|
assert_equal(false, h['id'].nil?)
|
|
@@ -81,32 +101,33 @@ class TestTrack < MiniTest::Test
|
|
|
81
101
|
assert_equal('hello world2', h['d'])
|
|
82
102
|
end
|
|
83
103
|
|
|
84
|
-
def
|
|
85
|
-
track1 =
|
|
104
|
+
def test_to_s
|
|
105
|
+
track1 = Track.new
|
|
86
106
|
track1.begin_time = Time.parse('1990-08-29 12:34:56')
|
|
87
|
-
assert_equal('1990-08-29 12:34 - xx:xx ', track1.
|
|
107
|
+
assert_equal('1990-08-29 12:34 - xx:xx ', track1.to_s)
|
|
88
108
|
|
|
89
|
-
task1 =
|
|
109
|
+
task1 = Task.new
|
|
90
110
|
task1.name = 'task1'
|
|
91
|
-
|
|
111
|
+
|
|
112
|
+
track1 = Track.new
|
|
92
113
|
track1.begin_time = Time.parse('1990-08-29 12:34:56')
|
|
93
114
|
track1.task = task1
|
|
94
|
-
assert_equal('1990-08-29 12:34 - xx:xx task1', track1.
|
|
115
|
+
assert_equal('1990-08-29 12:34 - xx:xx task1', track1.to_s)
|
|
95
116
|
|
|
96
117
|
track1.begin_time = Time.parse('1987-06-11 12:00:00')
|
|
97
118
|
track1.end_time = Time.parse('1987-06-12 23:00:00')
|
|
98
|
-
assert_equal('1987-06-11 12:00 - 23:00 1987-06-12 task1', track1.
|
|
119
|
+
assert_equal('1987-06-11 12:00 - 23:00 1987-06-12 task1', track1.to_s)
|
|
99
120
|
|
|
100
121
|
track1.description = 'hello world'
|
|
101
|
-
assert_equal('1987-06-11 12:00 - 23:00 1987-06-12 task1: hello world', track1.
|
|
122
|
+
assert_equal('1987-06-11 12:00 - 23:00 1987-06-12 task1: hello world', track1.to_s)
|
|
102
123
|
end
|
|
103
124
|
|
|
104
125
|
def test_from_h
|
|
105
|
-
track1 =
|
|
126
|
+
track1 = Track.from_h(nil, {})
|
|
106
127
|
assert_equal(nil, track1.begin_time)
|
|
107
128
|
assert_equal(nil, track1.end_time)
|
|
108
129
|
|
|
109
|
-
track1 =
|
|
130
|
+
track1 = Track.from_h(nil, {
|
|
110
131
|
'id' => 'abc',
|
|
111
132
|
'b' => '1986-06-18 12:34:56+0000',
|
|
112
133
|
'e' => '2014-11-11 19:05:12+0000',
|
data/tests/tc_window.rb
CHANGED
|
@@ -7,14 +7,17 @@ require 'timr'
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TestWindow < MiniTest::Test
|
|
10
|
+
|
|
11
|
+
include TheFox::Timr
|
|
12
|
+
|
|
10
13
|
def test_class_name
|
|
11
|
-
window1 =
|
|
14
|
+
window1 = Window.new
|
|
12
15
|
|
|
13
16
|
assert_equal('TheFox::Timr::Window', window1.class.to_s)
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def test_content_refresh
|
|
17
|
-
window1 =
|
|
20
|
+
window1 = Window.new
|
|
18
21
|
assert_equal(1, window1.content_refreshes)
|
|
19
22
|
|
|
20
23
|
window1.content_length = 10
|
|
@@ -35,7 +38,7 @@ class TestWindow < MiniTest::Test
|
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
def test_page_refreshes
|
|
38
|
-
window1 =
|
|
41
|
+
window1 = Window.new
|
|
39
42
|
assert_equal(0, window1.page_refreshes)
|
|
40
43
|
|
|
41
44
|
window1.content_changed
|
|
@@ -45,7 +48,7 @@ class TestWindow < MiniTest::Test
|
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
def test_page
|
|
48
|
-
window1 =
|
|
51
|
+
window1 = TestWindow.new
|
|
49
52
|
window1.content_length = 3
|
|
50
53
|
window1.content_refresh
|
|
51
54
|
page = window1.page.map{ |page_item| page_item[0..7] }
|
|
@@ -60,7 +63,7 @@ class TestWindow < MiniTest::Test
|
|
|
60
63
|
end
|
|
61
64
|
|
|
62
65
|
def test_page_object
|
|
63
|
-
window1 =
|
|
66
|
+
window1 = TestWindow.new
|
|
64
67
|
assert_equal(1, window1.cursor)
|
|
65
68
|
|
|
66
69
|
window1.content_length = 3
|
|
@@ -72,7 +75,7 @@ class TestWindow < MiniTest::Test
|
|
|
72
75
|
end
|
|
73
76
|
|
|
74
77
|
def test_has_next_page
|
|
75
|
-
window1 =
|
|
78
|
+
window1 = TestWindow.new
|
|
76
79
|
window1.content_length = 3
|
|
77
80
|
window1.content_refresh
|
|
78
81
|
assert_equal(true, window1.next_page?)
|
|
@@ -89,7 +92,7 @@ class TestWindow < MiniTest::Test
|
|
|
89
92
|
end
|
|
90
93
|
|
|
91
94
|
def test_has_previous_page
|
|
92
|
-
window1 =
|
|
95
|
+
window1 = TestWindow.new
|
|
93
96
|
window1.content_length = 3
|
|
94
97
|
window1.content_refresh
|
|
95
98
|
assert_equal(false, window1.previous_page?)
|
|
@@ -100,7 +103,7 @@ class TestWindow < MiniTest::Test
|
|
|
100
103
|
end
|
|
101
104
|
|
|
102
105
|
def test_jmp_next_previous_page
|
|
103
|
-
window1 =
|
|
106
|
+
window1 = TestWindow.new
|
|
104
107
|
window1.content_length = 3
|
|
105
108
|
window1.content_refresh
|
|
106
109
|
|
|
@@ -303,7 +306,7 @@ class TestWindow < MiniTest::Test
|
|
|
303
306
|
end
|
|
304
307
|
|
|
305
308
|
def test_cursor_border_top_bottom
|
|
306
|
-
window1 =
|
|
309
|
+
window1 = TestWindow.new
|
|
307
310
|
window1.content_length = 7
|
|
308
311
|
window1.content_refresh
|
|
309
312
|
assert_equal(1, window1.cursor_border_top)
|
|
@@ -343,7 +346,7 @@ class TestWindow < MiniTest::Test
|
|
|
343
346
|
end
|
|
344
347
|
|
|
345
348
|
def test_cursor_on_inner_range_multiple_pages
|
|
346
|
-
window1 =
|
|
349
|
+
window1 = TestWindow.new
|
|
347
350
|
window1.content_length = 7
|
|
348
351
|
window1.content_refresh
|
|
349
352
|
assert_equal(true, window1.cursor_on_inner_range?)
|
|
@@ -412,7 +415,7 @@ class TestWindow < MiniTest::Test
|
|
|
412
415
|
end
|
|
413
416
|
|
|
414
417
|
def test_cursor_on_inner_range_one_page
|
|
415
|
-
window1 =
|
|
418
|
+
window1 = TestWindow.new
|
|
416
419
|
window1.content_length = 35
|
|
417
420
|
window1.content_refresh
|
|
418
421
|
assert_equal(true, window1.cursor_on_inner_range?)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: timr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Christian Mayer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-06-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|