paradeiser 0.0.1 → 0.1.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/.gitignore +1 -1
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/Guardfile +17 -0
- data/README.md +76 -187
- data/Rakefile +12 -1
- data/TODO.md +62 -0
- data/VISION.md +474 -0
- data/bin/pom +60 -0
- data/doc/Paradeiser::Pomodoro_status.svg +50 -0
- data/lib/paradeiser.rb +25 -2
- data/lib/paradeiser/controllers/controller.rb +30 -0
- data/lib/paradeiser/controllers/paradeiser_controller.rb +10 -0
- data/lib/paradeiser/controllers/pomodori_controller.rb +38 -0
- data/lib/paradeiser/errors.rb +31 -0
- data/lib/paradeiser/executor.rb +15 -0
- data/lib/paradeiser/models/hook.rb +26 -0
- data/lib/paradeiser/models/job.rb +25 -0
- data/lib/paradeiser/models/pomodoro.rb +58 -0
- data/lib/paradeiser/models/repository.rb +57 -0
- data/lib/paradeiser/models/scheduler.rb +43 -0
- data/lib/paradeiser/refinements.rb +5 -0
- data/lib/paradeiser/router.rb +29 -0
- data/lib/paradeiser/version.rb +1 -1
- data/lib/paradeiser/view.rb +21 -0
- data/lib/paradeiser/views/paradeiser/init.erb +1 -0
- data/lib/paradeiser/views/pomodori/finish.erb +1 -0
- data/lib/paradeiser/views/pomodori/report.erb +5 -0
- data/lib/paradeiser/views/pomodori/start.erb +1 -0
- data/lib/paradeiser/views/pomodori/status.erb +9 -0
- data/paradeiser.gemspec +21 -4
- data/templates/linux/hooks/after-finish +10 -0
- data/templates/linux/hooks/after-start +7 -0
- data/templates/mac/hooks/after-finish +10 -0
- data/templates/mac/hooks/after-start +7 -0
- data/test/helper.rb +24 -0
- data/test/integration/test_pom.rb +17 -0
- data/test/lib/assertions.rb +10 -0
- data/test/lib/at_mock.rb +6 -0
- data/test/lib/options_mock.rb +7 -0
- data/test/lib/pomodoro_mock.rb +11 -0
- data/test/lib/process_status_mock.rb +2 -0
- data/test/lib/pstore_mock.rb +16 -0
- data/test/templates/hooks/pre-finish +1 -0
- data/test/unit/test_paradeiser_controller.rb +88 -0
- data/test/unit/test_pomodori_controller.rb +103 -0
- data/test/unit/test_pomodori_view.rb +78 -0
- data/test/unit/test_pomodoro.rb +100 -0
- data/test/unit/test_pomodoro_hooks.rb +83 -0
- data/test/unit/test_repository.rb +127 -0
- data/test/unit/test_router.rb +36 -0
- data/test/unit/test_scheduler.rb +44 -0
- metadata +244 -13
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestPomodoriView < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
@pom = Pomodoro.new
|
6
|
+
@pom.id = 1
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_status_idle
|
10
|
+
out, err = render(:status)
|
11
|
+
assert_match(/^Current state is idle.$/m, out)
|
12
|
+
assert_empty(err)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_status_active
|
16
|
+
start!
|
17
|
+
out, err = render(:status)
|
18
|
+
assert_match(/^Pomodoro #1 is active for another \d{1,2} minutes \(started at .*\)\.$/m, out)
|
19
|
+
assert_empty(err)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_status_finished
|
23
|
+
start!
|
24
|
+
finish!
|
25
|
+
out, err = render(:status)
|
26
|
+
assert_match(/^No active pomodoro. Last one was finished at .*\.$/m, out)
|
27
|
+
assert_empty(err)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_report_empty
|
31
|
+
@pom = []
|
32
|
+
out, err = render(:report)
|
33
|
+
assert_equal(1, out.lines.size)
|
34
|
+
assert_match(/^ID \| Status \| Started \| Ended$/m, out.lines.first)
|
35
|
+
assert_empty(err)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_report_one_active
|
39
|
+
start!
|
40
|
+
@pom = [@pom]
|
41
|
+
out, err = render(:report)
|
42
|
+
assert_equal(2, out.lines.size)
|
43
|
+
assert_match(/^ID \| Status \| Started \| Ended$/m, out.lines[0])
|
44
|
+
assert_match(/^1 \| active \| \d{1,2}:\d{1,2} \| $/m, out.lines[1])
|
45
|
+
assert_empty(err)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_report_one_finished
|
49
|
+
start!
|
50
|
+
finish!
|
51
|
+
@pom = [@pom]
|
52
|
+
out, err = render(:report)
|
53
|
+
assert_equal(2, out.lines.size)
|
54
|
+
assert_match(/^ID \| Status \| Started \| Ended$/m, out.lines[0])
|
55
|
+
assert_match(/^1 \| finished \| \d{1,2}:\d{1,2} \| \d{1,2}:\d{1,2}$/m, out.lines[1])
|
56
|
+
assert_empty(err)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_start
|
60
|
+
out, err = render(:start)
|
61
|
+
assert_match(/^Starting pomodoro #1\.$/m, out)
|
62
|
+
assert_empty(err)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_finish
|
66
|
+
start!
|
67
|
+
out, err = render(:finish)
|
68
|
+
assert_match(/^Finished pomodoro #1 after .* minutes\.$/m, out)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def render(method)
|
74
|
+
capture_io do
|
75
|
+
View.new('Pomodori', method).render(binding)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestPomodoro < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
@pom = Pomodoro.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_virgin
|
9
|
+
assert_equal(:idle, @pom.status_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_finish_idle
|
13
|
+
assert_raises StateMachine::InvalidTransition do
|
14
|
+
finish!
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_finish
|
19
|
+
start!
|
20
|
+
assert_equal(:active, @pom.status_name)
|
21
|
+
|
22
|
+
now = srand
|
23
|
+
|
24
|
+
Time.stub :now, Time.at(now) do
|
25
|
+
finish!
|
26
|
+
end
|
27
|
+
|
28
|
+
assert_equal(:finished, @pom.status_name)
|
29
|
+
assert_equal(now, @pom.finished_at.to_i)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_duration_idle
|
33
|
+
assert_equal(0, @pom.duration)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_duration_started
|
37
|
+
now = srand
|
38
|
+
|
39
|
+
Time.stub :now, Time.at(now) do
|
40
|
+
start!
|
41
|
+
end
|
42
|
+
|
43
|
+
later = now + rand(42)
|
44
|
+
|
45
|
+
Time.stub :now, Time.at(later) do
|
46
|
+
assert_equal(later - now, @pom.duration)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_duration_finished
|
51
|
+
now = srand
|
52
|
+
|
53
|
+
Time.stub :now, Time.at(now) do
|
54
|
+
start!
|
55
|
+
end
|
56
|
+
|
57
|
+
later = now + rand(42)
|
58
|
+
|
59
|
+
Time.stub :now, Time.at(later) do
|
60
|
+
finish!
|
61
|
+
end
|
62
|
+
|
63
|
+
assert_equal(later - now, @pom.duration)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_finish_finished
|
67
|
+
start!
|
68
|
+
finish!
|
69
|
+
assert_raises StateMachine::InvalidTransition do
|
70
|
+
finish!
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_start
|
75
|
+
now = srand
|
76
|
+
|
77
|
+
Time.stub :now, Time.at(now) do
|
78
|
+
start!
|
79
|
+
end
|
80
|
+
|
81
|
+
assert_equal(:active, @pom.status_name)
|
82
|
+
assert_equal(now, @pom.started_at.to_i)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_remaining
|
86
|
+
now = srand
|
87
|
+
|
88
|
+
Time.stub :now, Time.at(now) do
|
89
|
+
start!
|
90
|
+
assert_equal(Pomodoro::LENGTH_SECONDS, @pom.remaining)
|
91
|
+
end
|
92
|
+
|
93
|
+
delta = 600
|
94
|
+
later = now + delta
|
95
|
+
|
96
|
+
Time.stub :now, Time.at(later) do
|
97
|
+
assert_equal(Pomodoro::LENGTH_SECONDS - delta, @pom.remaining)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
class TestPomodoroHooks < MiniTest::Test
|
5
|
+
def setup
|
6
|
+
@orig_pom_dir = ENV['POM_DIR']
|
7
|
+
ENV['POM_DIR'] = Dir.mktmpdir
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
FileUtils.rm_rf(Paradeiser.pom_dir)
|
12
|
+
ENV['POM_DIR'] = @orig_pom_dir
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_before_finish_success
|
16
|
+
hook_name = 'before-finish'
|
17
|
+
|
18
|
+
pom = Pomodoro.new
|
19
|
+
pom.id = SecureRandom.random_number(1000)
|
20
|
+
pom.start
|
21
|
+
token_file = create_hook(hook_name)
|
22
|
+
refute_path_exists(token_file, "Token file must not exist before #{hook_name} hook is executed")
|
23
|
+
pom.finish
|
24
|
+
assert_path_exists(token_file, "#{hook_name} hook should have created a token file")
|
25
|
+
assert_equal(:finished, pom.status_name)
|
26
|
+
assert_equal("Pomodoro #{pom.id} started #{pom.started_at.strftime('%H:%M')}", File.read(token_file).chomp)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_before_finish_error
|
30
|
+
hook_name = 'before-finish'
|
31
|
+
|
32
|
+
pom = Pomodoro.new
|
33
|
+
pom.id = SecureRandom.random_number(1000)
|
34
|
+
pom.start
|
35
|
+
token_file = create_hook(hook_name, false)
|
36
|
+
refute_path_exists(token_file, "Token file must not exist before #{hook_name} hook is executed")
|
37
|
+
|
38
|
+
assert_raises HookFailedError do
|
39
|
+
pom.finish
|
40
|
+
end
|
41
|
+
|
42
|
+
refute_path_exists(token_file, "#{hook_name} hook should have created a token file")
|
43
|
+
assert_equal(:active, pom.status_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def create_hook(name, hook_succeeds = true)
|
49
|
+
token_file = File.join(Dir.tmpdir, SecureRandom.uuid)
|
50
|
+
|
51
|
+
hooks_dir = Paradeiser.hooks_dir
|
52
|
+
FileUtils.mkdir(hooks_dir)
|
53
|
+
|
54
|
+
hook_file = File.join(hooks_dir, name)
|
55
|
+
|
56
|
+
File.open(hook_file, 'w') do |f|
|
57
|
+
if hook_succeeds
|
58
|
+
f.write(hook_contents_success(token_file))
|
59
|
+
else
|
60
|
+
f.write(hook_contents_failure)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
File.chmod(0700, hook_file)
|
65
|
+
|
66
|
+
assert(File.exist?(hook_file))
|
67
|
+
token_file
|
68
|
+
end
|
69
|
+
|
70
|
+
def hook_contents_success(token_file)
|
71
|
+
hook_contents =<<"EOF"
|
72
|
+
#!/bin/sh
|
73
|
+
echo "Pomodoro $POM_ID started $POM_STARTED_AT" > #{token_file}
|
74
|
+
EOF
|
75
|
+
end
|
76
|
+
|
77
|
+
def hook_contents_failure
|
78
|
+
hook_contents =<<"EOF"
|
79
|
+
#!/bin/sh
|
80
|
+
exit 1
|
81
|
+
EOF
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestRepository < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
@backend = PStoreMock.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_save_idle
|
9
|
+
pom = Pomodoro.new
|
10
|
+
|
11
|
+
assert_raises IllegalStatusError do
|
12
|
+
invoke(:save, pom)
|
13
|
+
end
|
14
|
+
|
15
|
+
assert_equal(0, @backend.size)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_save_active
|
19
|
+
pom = Pomodoro.new
|
20
|
+
start!(pom)
|
21
|
+
|
22
|
+
invoke(:save, pom)
|
23
|
+
|
24
|
+
assert_equal(1, @backend.size)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_save_second_active
|
28
|
+
pom1 = Pomodoro.new
|
29
|
+
start!(pom1)
|
30
|
+
|
31
|
+
pom2 = Pomodoro.new
|
32
|
+
start!(pom2)
|
33
|
+
|
34
|
+
invoke(:save, pom1)
|
35
|
+
|
36
|
+
assert_raises SingletonError do
|
37
|
+
invoke(:save, pom2)
|
38
|
+
end
|
39
|
+
|
40
|
+
assert_equal(1, @backend.size)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_save_finished
|
44
|
+
pom = Pomodoro.new
|
45
|
+
start!(pom)
|
46
|
+
finish!(pom)
|
47
|
+
|
48
|
+
invoke(:save, pom)
|
49
|
+
|
50
|
+
assert_equal(1, @backend.size)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_save_active_finish_save
|
54
|
+
pom = Pomodoro.new
|
55
|
+
start!(pom)
|
56
|
+
|
57
|
+
invoke(:save, pom)
|
58
|
+
finish!(pom)
|
59
|
+
invoke(:save, pom)
|
60
|
+
|
61
|
+
assert_equal(1, @backend.size)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_all
|
65
|
+
@backend[:foo] = 'bar'
|
66
|
+
assert_equal(1, @backend.size)
|
67
|
+
|
68
|
+
all = invoke(:all)
|
69
|
+
|
70
|
+
assert_equal(1, all.size)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_any
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_find
|
77
|
+
@backend[:foo] = PomodoroMock.new(:length => 3)
|
78
|
+
@backend[:bar] = PomodoroMock.new(:length => 3)
|
79
|
+
@backend[:baz] = PomodoroMock.new(:length => 2)
|
80
|
+
assert_equal(3, @backend.size)
|
81
|
+
|
82
|
+
all = invoke(:find){|pom| pom.length == 3}
|
83
|
+
|
84
|
+
assert_equal(2, all.size)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_active
|
88
|
+
@backend[:foo] = PomodoroMock.new(:active => false)
|
89
|
+
@backend[:bar] = PomodoroMock.new(:active => true)
|
90
|
+
@backend[:baz] = PomodoroMock.new(:active => false)
|
91
|
+
|
92
|
+
active = invoke(:active)
|
93
|
+
assert_equal(@backend[:bar], active)
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_active_true
|
97
|
+
@backend[:foo] = PomodoroMock.new(:active => false)
|
98
|
+
@backend[:bar] = PomodoroMock.new(:active => true)
|
99
|
+
@backend[:baz] = PomodoroMock.new(:active => false)
|
100
|
+
|
101
|
+
assert(invoke(:active?))
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_active_false
|
105
|
+
@backend[:foo] = PomodoroMock.new(:active => false)
|
106
|
+
@backend[:bar] = PomodoroMock.new(:active => false)
|
107
|
+
@backend[:baz] = PomodoroMock.new(:active => false)
|
108
|
+
|
109
|
+
refute(invoke(:active?))
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_corrupted_repo
|
113
|
+
@backend[:foo] = PomodoroMock.new(:active => true)
|
114
|
+
@backend[:bar] = PomodoroMock.new(:active => true)
|
115
|
+
|
116
|
+
assert_raises SingletonError do
|
117
|
+
invoke(:active?)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
private
|
121
|
+
|
122
|
+
def invoke(method, *args, &blk)
|
123
|
+
Repository.stub :backend, @backend do
|
124
|
+
Repository.send(method, *args, &blk)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'fakefs/safe'
|
3
|
+
|
4
|
+
class CommandMock < Struct.new(:name)
|
5
|
+
end
|
6
|
+
|
7
|
+
class TestRouter < MiniTest::Test
|
8
|
+
def setup
|
9
|
+
FakeFS.activate!
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
FakeFS.deactivate!
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_init
|
17
|
+
refute(Dir.exists?(Paradeiser.pom_dir), "Expect #{Paradeiser.pom_dir} to not exist yet")
|
18
|
+
|
19
|
+
block = Router.new.dispatch(CommandMock.new(:init))
|
20
|
+
refute_nil(block)
|
21
|
+
|
22
|
+
refute(Dir.exists?(Paradeiser.pom_dir), "Expect #{Paradeiser.pom_dir} to not exist yet")
|
23
|
+
|
24
|
+
# fake the view
|
25
|
+
dirname = File.join(File.dirname(__FILE__), '..', '..', 'lib', 'paradeiser', 'views', 'paradeiser')
|
26
|
+
FileUtils.mkdir_p(dirname)
|
27
|
+
view = File.join(dirname, 'init.erb')
|
28
|
+
FileUtils.touch(view)
|
29
|
+
|
30
|
+
begin
|
31
|
+
block.call(nil, OptionsMock.new(:trace => true, :verbose => false))
|
32
|
+
ensure
|
33
|
+
FileUtils.rm_r(Paradeiser.pom_dir, :force => true)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestScheduler < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
if Scheduler.list.any?
|
6
|
+
@do_not_clear = true
|
7
|
+
raise "The at queue 't' is not empty. Clean it up before running this test again."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
Scheduler.clear unless @do_not_clear
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_add
|
16
|
+
result = Scheduler.list
|
17
|
+
assert_equal(0, result.size)
|
18
|
+
|
19
|
+
job = Scheduler.add(:finish, 42)
|
20
|
+
refute_nil(job)
|
21
|
+
refute_nil(job.id)
|
22
|
+
assert(0 < job.id.to_i)
|
23
|
+
|
24
|
+
list = Scheduler.list
|
25
|
+
assert_equal(1, list.size)
|
26
|
+
assert_equal(job, list.first)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_clear
|
30
|
+
n = 3
|
31
|
+
|
32
|
+
n.times.each do |spec|
|
33
|
+
Scheduler.add(:finish, 43)
|
34
|
+
end
|
35
|
+
|
36
|
+
result = Scheduler.list
|
37
|
+
assert_equal(n, result.size)
|
38
|
+
|
39
|
+
Scheduler.clear
|
40
|
+
|
41
|
+
result = Scheduler.list
|
42
|
+
assert_equal(0, result.size)
|
43
|
+
end
|
44
|
+
end
|