paradeiser 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/README.md +96 -35
  4. data/TODO.md +14 -15
  5. data/VISION.md +92 -84
  6. data/bin/{pom → par} +32 -2
  7. data/doc/Paradeiser::Break_status.svg +50 -0
  8. data/doc/Paradeiser::Pomodoro_status.svg +40 -22
  9. data/lib/paradeiser.rb +3 -3
  10. data/lib/paradeiser/controllers/breaks_controller.rb +19 -0
  11. data/lib/paradeiser/controllers/controller.rb +3 -3
  12. data/lib/paradeiser/controllers/paradeiser_controller.rb +13 -2
  13. data/lib/paradeiser/controllers/pomodori_controller.rb +36 -17
  14. data/lib/paradeiser/errors.rb +12 -6
  15. data/lib/paradeiser/executor.rb +2 -0
  16. data/lib/paradeiser/initializers/inflections.rb +4 -0
  17. data/lib/paradeiser/models/break.rb +40 -0
  18. data/lib/paradeiser/models/hook.rb +4 -4
  19. data/lib/paradeiser/models/interrupt.rb +18 -0
  20. data/lib/paradeiser/models/job.rb +1 -1
  21. data/lib/paradeiser/models/pomodoro.rb +30 -22
  22. data/lib/paradeiser/models/repository.rb +26 -13
  23. data/lib/paradeiser/models/scheduled.rb +25 -0
  24. data/lib/paradeiser/models/scheduler.rb +1 -1
  25. data/lib/paradeiser/models/status.rb +21 -0
  26. data/lib/paradeiser/{refinements.rb → refinements/numeric.rb} +4 -0
  27. data/lib/paradeiser/router.rb +12 -7
  28. data/lib/paradeiser/version.rb +1 -1
  29. data/lib/paradeiser/views/paradeiser/init.erb +1 -1
  30. data/lib/paradeiser/views/paradeiser/report.erb +5 -0
  31. data/lib/paradeiser/views/paradeiser/status.erb +13 -0
  32. data/paradeiser.gemspec +2 -0
  33. data/templates/linux/hooks/after-finish-break +10 -0
  34. data/templates/linux/hooks/after-finish-pomodoro +10 -0
  35. data/templates/linux/hooks/after-start-break +7 -0
  36. data/templates/linux/hooks/after-start-pomodoro +7 -0
  37. data/templates/mac/hooks/after-finish-break +10 -0
  38. data/templates/mac/hooks/after-finish-pomodoro +10 -0
  39. data/templates/mac/hooks/after-start-break +7 -0
  40. data/templates/mac/hooks/after-start-pomodoro +7 -0
  41. data/test/helper.rb +37 -4
  42. data/test/integration/{test_pom.rb → test_par.rb} +4 -4
  43. data/test/lib/{pomodoro_mock.rb → schedulable_mock.rb} +9 -1
  44. data/test/unit/test_break.rb +99 -0
  45. data/test/unit/test_break_controller.rb +56 -0
  46. data/test/unit/test_interrupt.rb +36 -0
  47. data/test/unit/test_paradeiser_controller_init.rb +92 -0
  48. data/test/unit/test_paradeiser_controller_report.rb +44 -0
  49. data/test/unit/test_paradeiser_controller_status.rb +70 -0
  50. data/test/unit/test_paradeiser_view.rb +66 -0
  51. data/test/unit/test_pomodori_controller.rb +87 -31
  52. data/test/unit/test_pomodori_view.rb +0 -50
  53. data/test/unit/test_pomodoro.rb +131 -9
  54. data/test/unit/test_pomodoro_hooks.rb +165 -17
  55. data/test/unit/test_repository.rb +38 -15
  56. data/test/unit/test_router.rb +4 -4
  57. data/test/unit/test_status.rb +26 -0
  58. metadata +70 -17
  59. data/lib/paradeiser/views/pomodori/report.erb +0 -5
  60. data/lib/paradeiser/views/pomodori/status.erb +0 -9
  61. data/templates/linux/hooks/after-finish +0 -10
  62. data/templates/linux/hooks/after-start +0 -7
  63. data/templates/mac/hooks/after-finish +0 -10
  64. data/templates/mac/hooks/after-start +0 -7
  65. data/test/unit/test_paradeiser_controller.rb +0 -88
@@ -1,16 +1,16 @@
1
1
  require 'helper'
2
2
 
3
- class TestPom < MiniTest::Test
4
- POM_BIN = 'pom'
3
+ class TestPar < MiniTest::Test
4
+ PAR = 'bin/par'
5
5
 
6
6
  def test_no_args_defaults_to_help
7
- out, err, status = Open3.capture3(POM_BIN)
7
+ out, err, status = Open3.capture3(PAR)
8
8
  assert_equal(0, status.exitstatus, "Expected exit status to be 0, but it was #{status.exitstatus}. STDERR contains: #{err}")
9
9
  assert_empty(err)
10
10
  end
11
11
 
12
12
  def test_unknown
13
- out, err, status = Open3.capture3("#{POM_BIN} unknown")
13
+ out, err, status = Open3.capture3("#{PAR} unknown")
14
14
  refute_equal(0, status.exitstatus)
15
15
  refute_empty(err)
16
16
  end
@@ -1,6 +1,6 @@
1
1
  require 'ostruct'
2
2
 
3
- class PomodoroMock < OpenStruct
3
+ class SchedulableMock < OpenStruct
4
4
  def initialize(attributes)
5
5
  super(attributes)
6
6
  end
@@ -8,4 +8,12 @@ class PomodoroMock < OpenStruct
8
8
  def active?
9
9
  !!active
10
10
  end
11
+
12
+ def finished?
13
+ !!finished
14
+ end
15
+
16
+ def canceled?
17
+ !!canceled
18
+ end
11
19
  end
@@ -0,0 +1,99 @@
1
+ require 'helper'
2
+
3
+ class TestBreak < MiniTest::Test
4
+ def setup
5
+ @break = Break.new
6
+ end
7
+
8
+ def test_virgin
9
+ assert_equal(:idle, @break.status_name)
10
+ end
11
+
12
+ def test_break
13
+ start!
14
+ assert_equal(:active, @break.status_name)
15
+ end
16
+
17
+ def test_finish_idle
18
+ assert_raises StateMachine::InvalidTransition do
19
+ finish!
20
+ end
21
+ end
22
+
23
+ def test_finish_break
24
+ start!
25
+ assert_equal(:active, @break.status_name)
26
+
27
+ now = srand
28
+
29
+ Time.stub :now, Time.at(now) do
30
+ finish!
31
+ end
32
+
33
+ assert_equal(:finished, @break.status_name)
34
+ assert_equal(now, @break.finished_at.to_i)
35
+ end
36
+
37
+ def test_length
38
+ assert_equal(5 * 60, @break.length)
39
+ end
40
+
41
+
42
+ def test_duration_idle
43
+ assert_equal(0, @break.duration)
44
+ end
45
+
46
+ def test_duration
47
+ now = srand
48
+
49
+ Time.stub :now, Time.at(now) do
50
+ start!
51
+ end
52
+
53
+ later = now + rand(42)
54
+
55
+ Time.stub :now, Time.at(later) do
56
+ assert_equal(later - now, @break.duration)
57
+ end
58
+ end
59
+
60
+ def test_duration_finished
61
+ now = srand
62
+
63
+ Time.stub :now, Time.at(now) do
64
+ start!
65
+ end
66
+
67
+ later = now + rand(42)
68
+
69
+ Time.stub :now, Time.at(later) do
70
+ finish!
71
+ end
72
+
73
+ assert_equal(later - now, @break.duration)
74
+ end
75
+
76
+ def test_finish_break
77
+ start!
78
+ finish!
79
+ assert_raises StateMachine::InvalidTransition do
80
+ finish!
81
+ end
82
+ end
83
+
84
+ def test_remaining
85
+ now = srand
86
+
87
+ Time.stub :now, Time.at(now) do
88
+ start!
89
+ assert_equal(@break.length, @break.remaining)
90
+ end
91
+
92
+ delta = 120
93
+ later = now + delta
94
+
95
+ Time.stub :now, Time.at(later) do
96
+ assert_equal(@break.length - delta, @break.remaining)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,56 @@
1
+ require 'helper'
2
+
3
+ class TestBreakController < MiniTest::Test
4
+ def setup
5
+ @backend = PStoreMock.new
6
+ end
7
+
8
+ def test_break
9
+ br3k, has_output = invoke(:start, '@break', 'has_output')
10
+ assert_equal(:active, br3k.status_name)
11
+ assert_equal(false, has_output)
12
+ assert_equal(1, @backend.size)
13
+ end
14
+
15
+ def test_break_active
16
+ invoke(:start)
17
+ assert_equal(1, @backend.size)
18
+
19
+ assert_raises SingletonError do
20
+ invoke(:start)
21
+ end
22
+ assert_equal(1, @backend.size)
23
+ end
24
+
25
+ def test_finish
26
+ invoke(:start)
27
+ br3ak, has_output = invoke(:finish, '@break', 'has_output')
28
+ assert_equal(:finished, br3ak.status_name)
29
+ assert_equal(false, has_output)
30
+ assert_equal(1, @backend.size)
31
+ end
32
+
33
+ def test_finish_idle
34
+ assert_raises NotActiveError do
35
+ invoke(:finish)
36
+ end
37
+ assert_equal(0, @backend.size)
38
+ end
39
+ private
40
+
41
+ def invoke(method, *attributes)
42
+ controller = BreaksController.new(method)
43
+
44
+ Repository.stub :backend, @backend do
45
+ Scheduler.stub(:add, nil) do
46
+ Scheduler.stub(:clear, nil) do
47
+ controller.call(nil, nil)
48
+ end
49
+ end
50
+ end
51
+
52
+ attributes.map do |attribute|
53
+ controller.get_binding.eval(attribute)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ class TestInterrupt < MiniTest::Test
4
+ def setup
5
+ @now = srand
6
+ end
7
+
8
+ def test_created_at
9
+ int = new_interrupt
10
+ assert_equal(@now, int.created_at.to_i)
11
+ end
12
+
13
+ def test_defaults_to_internal
14
+ int = new_interrupt
15
+ assert_equal(:internal, int.type)
16
+ end
17
+
18
+ def test_external
19
+ int = new_interrupt(:external)
20
+ assert_equal(:external, int.type)
21
+ end
22
+
23
+ def test_unknown
24
+ assert_raises InvalidTypeError do
25
+ new_interrupt(:unknown)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def new_interrupt(type = nil)
32
+ Time.stub :now, Time.at(@now) do
33
+ Paradeiser::Interrupt.new(type)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,92 @@
1
+ require 'helper'
2
+ require 'fakefs/safe'
3
+
4
+ class TestParadeiserControllerInit < MiniTest::Test
5
+ HOOKS = ['after-finish', 'before-finish']
6
+
7
+ def setup
8
+ @orig_PAR_DIR = ENV['PAR_DIR']
9
+ FakeFS.activate!
10
+ create_hook_templates
11
+ end
12
+
13
+ def teardown
14
+ FileUtils.rm_r(Paradeiser.par_dir, :force => true) if Dir.exists?(Paradeiser.par_dir)
15
+ FakeFS.deactivate!
16
+ ENV['PAR_DIR'] = @orig_PAR_DIR
17
+ end
18
+
19
+ def test_init_virgin
20
+ ENV.delete('PAR_DIR')
21
+ refute(Dir.exists?(Paradeiser.par_dir), "Expect #{Paradeiser.par_dir} to not exist yet")
22
+
23
+ invoke(:init)
24
+ assert(Dir.exists?(Paradeiser.par_dir))
25
+ assert_hooks_exist
26
+ end
27
+
28
+ def test_init_existing
29
+ FileUtils.mkdir_p(Paradeiser.par_dir, 0700)
30
+ assert(Dir.exists?(Paradeiser.par_dir))
31
+
32
+ invoke(:init)
33
+ assert(Dir.exists?(Paradeiser.par_dir))
34
+ assert_hooks_exist
35
+ end
36
+
37
+ def test_init_virgin_with_env_override
38
+ dir = tempdir_name
39
+ refute_equal(dir, Paradeiser.par_dir)
40
+ ENV['PAR_DIR'] = dir
41
+ assert_equal(dir, Paradeiser.par_dir)
42
+ refute(Dir.exists?(Paradeiser.par_dir), "PAR_DIR override #{Paradeiser.par_dir} must not exist")
43
+
44
+ invoke(:init)
45
+
46
+ assert(Dir.exists?(Paradeiser.par_dir))
47
+ assert_hooks_exist
48
+ end
49
+
50
+ def test_init_existing_with_env_override
51
+ ENV.delete('PAR_DIR')
52
+
53
+ Dir.mktmpdir do |dir|
54
+ refute_equal(dir, Paradeiser.par_dir)
55
+ ENV['PAR_DIR'] = dir
56
+ assert_equal(dir, Paradeiser.par_dir)
57
+ assert(Dir.exists?(Paradeiser.par_dir), "PAR_DIR override #{Paradeiser.par_dir} must exist")
58
+
59
+ invoke(:init)
60
+ assert(Dir.exists?(Paradeiser.par_dir), "PAR_DIR override #{Paradeiser.par_dir} must exist")
61
+ assert_hooks_exist
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def invoke(verb)
68
+ ParadeiserController.new(verb).call(nil, nil)
69
+ end
70
+
71
+ # Makes a temporary directory name, but does not create the directory
72
+ def tempdir_name
73
+ dir = File.join(Dir.tmpdir, SecureRandom.uuid)
74
+ refute(Dir.exists?(dir))
75
+ dir
76
+ end
77
+
78
+ def create_hook_templates
79
+ hook_templates_dir = File.join(Paradeiser.templates_dir, Paradeiser.os.to_s, 'hooks')
80
+ FileUtils.mkdir_p(hook_templates_dir)
81
+
82
+ HOOKS.each do |hook|
83
+ FileUtils.touch(File.join(hook_templates_dir, hook))
84
+ end
85
+ end
86
+
87
+ def assert_hooks_exist
88
+ HOOKS.each do |hook|
89
+ assert(File.exist?(File.join(Paradeiser.hooks_dir, hook)))
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,44 @@
1
+ require 'helper'
2
+
3
+ class TestParadeiserControllerReport < ControllerTest
4
+ def setup
5
+ @backend = PStoreMock.new
6
+ end
7
+
8
+ def test_idle
9
+ br3ak, has_output = invoke(:report, '@pom', 'has_output')
10
+ assert_empty(br3ak)
11
+ assert_equal(true, has_output)
12
+ end
13
+
14
+ def test_active_break
15
+ @backend[:foo] = SchedulableMock.new(:status_name => :active)
16
+
17
+ br3ak, has_output = invoke(:report, '@pom', 'has_output')
18
+ assert_equal(1, br3ak.size)
19
+ assert_equal(true, has_output)
20
+ end
21
+
22
+ def test_finished_break
23
+ @backend[:foo] = SchedulableMock.new(:status_name => :finished)
24
+ @backend[:bar] = SchedulableMock.new(:status_name => :active)
25
+ br3ak, has_output = invoke(:report, '@pom', 'has_output')
26
+ assert_equal(2, br3ak.size)
27
+ assert_equal(true, has_output)
28
+ end
29
+
30
+ def test_active_pom
31
+ @backend[:bar] = SchedulableMock.new(:status_name => :active)
32
+ pomodori, has_output = invoke(:report, '@pom', 'has_output')
33
+ assert_equal(1, pomodori.size)
34
+ assert_equal(true, has_output)
35
+ end
36
+
37
+ def test_finished_pom
38
+ @backend[:foo] = SchedulableMock.new(:status_name => :finished)
39
+ @backend[:bar] = SchedulableMock.new(:status_name => :active)
40
+ pomodori, has_output = invoke(:report, '@pom', 'has_output')
41
+ assert_equal(2, pomodori.size)
42
+ assert_equal(true, has_output)
43
+ end
44
+ end
@@ -0,0 +1,70 @@
1
+ require 'helper'
2
+
3
+ class TestParadeiserControllerStatus < ControllerTest
4
+ def setup
5
+ @backend = PStoreMock.new
6
+ end
7
+
8
+ def test_status_idle
9
+ exitstatus, has_output = invoke(:status, 'exitstatus', 'has_output')
10
+ assert_equal(-1, exitstatus)
11
+ assert_equal(true, has_output)
12
+ assert_equal(0, @backend.size)
13
+ end
14
+
15
+ def test_status_active_pom
16
+ @backend[:foo] = SchedulableMock.new(
17
+ :status => 'active',
18
+ :status_name => :active,
19
+ :active => true,
20
+ :name => 'pomodoro')
21
+
22
+ pom, has_output, exitstatus = invoke(:status, '@pom', 'has_output', 'exitstatus')
23
+ assert_equal(0, exitstatus)
24
+ assert_equal(:active, pom.status_name)
25
+ assert_equal(true, has_output)
26
+ assert_equal(1, @backend.size)
27
+ end
28
+
29
+ def test_status_finished_pom
30
+ @backend[:foo] = SchedulableMock.new(
31
+ :status => 'finished',
32
+ :status_name => :finished,
33
+ :finished => true,
34
+ :name => 'pomodoro')
35
+
36
+ pom, has_output, exitstatus = invoke(:status, '@pom', 'has_output', 'exitstatus')
37
+ assert_equal(1, exitstatus)
38
+ assert_equal(:finished, pom.status_name)
39
+ assert_equal(true, has_output)
40
+ assert_equal(1, @backend.size)
41
+ end
42
+
43
+ def test_status_active_break
44
+ @backend[:foo] = SchedulableMock.new(
45
+ :status => 'active',
46
+ :status_name => :active,
47
+ :active => true,
48
+ :name => 'break')
49
+
50
+ pom, has_output, exitstatus = invoke(:status, '@pom', 'has_output', 'exitstatus')
51
+ assert_equal(2, exitstatus)
52
+ assert_equal(:active, pom.status_name)
53
+ assert_equal(true, has_output)
54
+ assert_equal(1, @backend.size)
55
+ end
56
+
57
+ def test_status_finished_break
58
+ @backend[:foo] = SchedulableMock.new(
59
+ :status => 'finished',
60
+ :status_name => :finished,
61
+ :finished => true,
62
+ :name => 'break')
63
+
64
+ pom, has_output, exitstatus = invoke(:status, '@pom', 'has_output', 'exitstatus')
65
+ assert_equal(3, exitstatus)
66
+ assert_equal(:finished, pom.status_name)
67
+ assert_equal(true, has_output)
68
+ assert_equal(1, @backend.size)
69
+ end
70
+ end
@@ -0,0 +1,66 @@
1
+ require 'helper'
2
+
3
+ class TestParadeiserView < 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(/^Nothing active. Last pomodoro 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 \| Name \| Status \| Started \| Ended | Interrupts$/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 \| Name \| Status \| Started \| Ended | Interrupts$/m, out.lines[0])
44
+ assert_match(/^1 \| pomodoro \| active \| \d{1,2}:\d{1,2} \| \| \d+ I, \d+ E$/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 \| Name \| Status \| Started \| Ended | Interrupts$/m, out.lines[0])
55
+ assert_match(/^1 \| pomodoro \| finished \| \d{1,2}:\d{1,2} \| \d{1,2}:\d{1,2} \| \d+ I, \d+ E$/m, out.lines[1])
56
+ assert_empty(err)
57
+ end
58
+
59
+ private
60
+
61
+ def render(method)
62
+ capture_io do
63
+ View.new('Paradeiser', method).render(binding)
64
+ end
65
+ end
66
+ end