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.
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