paradeiser 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +8 -0
  4. data/Gemfile +6 -0
  5. data/Guardfile +17 -0
  6. data/README.md +76 -187
  7. data/Rakefile +12 -1
  8. data/TODO.md +62 -0
  9. data/VISION.md +474 -0
  10. data/bin/pom +60 -0
  11. data/doc/Paradeiser::Pomodoro_status.svg +50 -0
  12. data/lib/paradeiser.rb +25 -2
  13. data/lib/paradeiser/controllers/controller.rb +30 -0
  14. data/lib/paradeiser/controllers/paradeiser_controller.rb +10 -0
  15. data/lib/paradeiser/controllers/pomodori_controller.rb +38 -0
  16. data/lib/paradeiser/errors.rb +31 -0
  17. data/lib/paradeiser/executor.rb +15 -0
  18. data/lib/paradeiser/models/hook.rb +26 -0
  19. data/lib/paradeiser/models/job.rb +25 -0
  20. data/lib/paradeiser/models/pomodoro.rb +58 -0
  21. data/lib/paradeiser/models/repository.rb +57 -0
  22. data/lib/paradeiser/models/scheduler.rb +43 -0
  23. data/lib/paradeiser/refinements.rb +5 -0
  24. data/lib/paradeiser/router.rb +29 -0
  25. data/lib/paradeiser/version.rb +1 -1
  26. data/lib/paradeiser/view.rb +21 -0
  27. data/lib/paradeiser/views/paradeiser/init.erb +1 -0
  28. data/lib/paradeiser/views/pomodori/finish.erb +1 -0
  29. data/lib/paradeiser/views/pomodori/report.erb +5 -0
  30. data/lib/paradeiser/views/pomodori/start.erb +1 -0
  31. data/lib/paradeiser/views/pomodori/status.erb +9 -0
  32. data/paradeiser.gemspec +21 -4
  33. data/templates/linux/hooks/after-finish +10 -0
  34. data/templates/linux/hooks/after-start +7 -0
  35. data/templates/mac/hooks/after-finish +10 -0
  36. data/templates/mac/hooks/after-start +7 -0
  37. data/test/helper.rb +24 -0
  38. data/test/integration/test_pom.rb +17 -0
  39. data/test/lib/assertions.rb +10 -0
  40. data/test/lib/at_mock.rb +6 -0
  41. data/test/lib/options_mock.rb +7 -0
  42. data/test/lib/pomodoro_mock.rb +11 -0
  43. data/test/lib/process_status_mock.rb +2 -0
  44. data/test/lib/pstore_mock.rb +16 -0
  45. data/test/templates/hooks/pre-finish +1 -0
  46. data/test/unit/test_paradeiser_controller.rb +88 -0
  47. data/test/unit/test_pomodori_controller.rb +103 -0
  48. data/test/unit/test_pomodori_view.rb +78 -0
  49. data/test/unit/test_pomodoro.rb +100 -0
  50. data/test/unit/test_pomodoro_hooks.rb +83 -0
  51. data/test/unit/test_repository.rb +127 -0
  52. data/test/unit/test_router.rb +36 -0
  53. data/test/unit/test_scheduler.rb +44 -0
  54. 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