nanoc 4.7.0 → 4.7.1

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +4 -4
  3. data/NEWS.md +12 -1
  4. data/README.md +1 -1
  5. data/lib/nanoc/base/entities/directed_graph.rb +16 -0
  6. data/lib/nanoc/base/entities/identifiable_collection.rb +24 -8
  7. data/lib/nanoc/base/entities/outdatedness_reasons.rb +5 -0
  8. data/lib/nanoc/base/errors.rb +11 -6
  9. data/lib/nanoc/base/memoization.rb +6 -23
  10. data/lib/nanoc/base/services/compiler/phases/abstract.rb +3 -3
  11. data/lib/nanoc/base/services/compiler/phases/cache.rb +1 -3
  12. data/lib/nanoc/base/services/compiler/phases/mark_done.rb +1 -3
  13. data/lib/nanoc/base/services/compiler/phases/recalculate.rb +1 -3
  14. data/lib/nanoc/base/services/compiler/phases/resume.rb +1 -3
  15. data/lib/nanoc/base/services/compiler/phases/write.rb +1 -3
  16. data/lib/nanoc/base/services/filter.rb +15 -0
  17. data/lib/nanoc/base/services/item_rep_selector.rb +1 -1
  18. data/lib/nanoc/base/services/outdatedness_checker.rb +3 -1
  19. data/lib/nanoc/base/services/outdatedness_rule.rb +7 -0
  20. data/lib/nanoc/base/services/outdatedness_rules.rb +16 -0
  21. data/lib/nanoc/cli/commands/compile.rb +12 -411
  22. data/lib/nanoc/cli/commands/compile_listeners/abstract.rb +30 -0
  23. data/lib/nanoc/cli/commands/compile_listeners/debug_printer.rb +34 -0
  24. data/lib/nanoc/cli/commands/compile_listeners/diff_generator.rb +91 -0
  25. data/lib/nanoc/cli/commands/compile_listeners/file_action_printer.rb +61 -0
  26. data/lib/nanoc/cli/commands/compile_listeners/stack_prof_profiler.rb +22 -0
  27. data/lib/nanoc/cli/commands/compile_listeners/timing_recorder.rb +174 -0
  28. data/lib/nanoc/filters/xsl.rb +2 -0
  29. data/lib/nanoc/version.rb +1 -1
  30. data/spec/nanoc/base/directed_graph_spec.rb +54 -0
  31. data/spec/nanoc/base/errors/dependency_cycle_spec.rb +32 -0
  32. data/spec/nanoc/base/filter_spec.rb +36 -0
  33. data/spec/nanoc/base/services/compiler/phases/abstract_spec.rb +23 -11
  34. data/spec/nanoc/base/services/outdatedness_rules_spec.rb +41 -0
  35. data/spec/nanoc/cli/commands/compile/file_action_printer_spec.rb +1 -1
  36. data/spec/nanoc/cli/commands/compile/timing_recorder_spec.rb +124 -54
  37. data/spec/nanoc/cli/commands/compile_spec.rb +1 -1
  38. data/spec/nanoc/regressions/gh_1040_spec.rb +1 -1
  39. data/spec/nanoc/regressions/gh_924_spec.rb +89 -0
  40. data/test/base/test_compiler.rb +1 -1
  41. data/test/base/test_memoization.rb +6 -0
  42. data/test/cli/commands/test_compile.rb +2 -2
  43. metadata +12 -3
@@ -0,0 +1,32 @@
1
+ describe Nanoc::Int::Errors::DependencyCycle do
2
+ subject(:error) { described_class.new(graph) }
3
+
4
+ let(:graph) do
5
+ Nanoc::Int::DirectedGraph.new([]).tap do |g|
6
+ g.add_edge(rep_a, rep_b)
7
+ g.add_edge(rep_b, rep_c)
8
+ g.add_edge(rep_c, rep_d)
9
+ g.add_edge(rep_d, rep_e)
10
+ g.add_edge(rep_e, rep_b)
11
+ end
12
+ end
13
+
14
+ let(:rep_a) { Nanoc::Int::ItemRep.new(Nanoc::Int::Item.new('a', {}, '/a.md'), :default) }
15
+ let(:rep_b) { Nanoc::Int::ItemRep.new(Nanoc::Int::Item.new('b', {}, '/b.md'), :default) }
16
+ let(:rep_c) { Nanoc::Int::ItemRep.new(Nanoc::Int::Item.new('c', {}, '/c.md'), :default) }
17
+ let(:rep_d) { Nanoc::Int::ItemRep.new(Nanoc::Int::Item.new('d', {}, '/d.md'), :default) }
18
+ let(:rep_e) { Nanoc::Int::ItemRep.new(Nanoc::Int::Item.new('e', {}, '/e.md'), :default) }
19
+
20
+ it 'has an informative error message' do
21
+ expected = <<EOS
22
+ The site cannot be compiled because there is a dependency cycle:
23
+
24
+ (1) item /b.md, rep :default, depends on
25
+ (2) item /c.md, rep :default, depends on
26
+ (3) item /d.md, rep :default, depends on
27
+ (4) item /e.md, rep :default, depends on (1)
28
+ EOS
29
+
30
+ expect(error.message).to eql(expected)
31
+ end
32
+ end
@@ -37,6 +37,42 @@ describe Nanoc::Filter do
37
37
  end
38
38
  end
39
39
 
40
+ describe '.always_outdated? + .always_outdated' do
41
+ context 'not always outdated' do
42
+ let(:filter_class) do
43
+ Class.new(Nanoc::Filter) do
44
+ identifier :bea22a356b6b031cea1e615087179803818c6a53
45
+
46
+ def run(content, _params)
47
+ content.upcase
48
+ end
49
+ end
50
+ end
51
+
52
+ it 'is not always outdated' do
53
+ expect(filter_class).not_to be_always_outdated
54
+ end
55
+ end
56
+
57
+ context 'always outdated' do
58
+ let(:filter_class) do
59
+ Class.new(Nanoc::Filter) do
60
+ identifier :d7413fa71223e5e69b03a0abfa25806e07e14f3a
61
+
62
+ always_outdated
63
+
64
+ def run(content, _params)
65
+ content.upcase
66
+ end
67
+ end
68
+ end
69
+
70
+ it 'is always outdated' do
71
+ expect(filter_class).to be_always_outdated
72
+ end
73
+ end
74
+ end
75
+
40
76
  describe '#depend_on' do
41
77
  subject { filter.depend_on(item_views) }
42
78
 
@@ -1,6 +1,6 @@
1
1
  describe Nanoc::Int::Compiler::Phases::Abstract do
2
2
  subject(:phase) do
3
- described_class.new(wrapped: wrapped, name: 'my_phase')
3
+ described_class.new(wrapped: wrapped)
4
4
  end
5
5
 
6
6
  let(:item) { Nanoc::Int::Item.new('foo', {}, '/stuff.md') }
@@ -19,29 +19,41 @@ describe Nanoc::Int::Compiler::Phases::Abstract do
19
19
  describe '#call' do
20
20
  subject { phase.call(rep, is_outdated: false) }
21
21
 
22
- let(:phase) do
22
+ let(:phase_class) do
23
23
  Class.new(described_class) do
24
+ def self.to_s
25
+ 'AbstractSpec::MyTestingPhaseClass'
26
+ end
27
+
24
28
  def run(_rep, is_outdated:) # rubocop:disable Lint/UnusedMethodArgument
25
29
  yield
26
30
  end
27
- end.new(wrapped: wrapped, name: 'my_phase')
31
+ end
28
32
  end
29
33
 
30
- let(:wrapped) do
34
+ let(:phase) { phase_class.new(wrapped: wrapped) }
35
+
36
+ let(:wrapped_class) do
31
37
  Class.new(described_class) do
38
+ def self.to_s
39
+ 'AbstractSpec::MyTestingWrappedPhaseClass'
40
+ end
41
+
32
42
  def run(_rep, is_outdated:); end
33
- end.new(wrapped: nil, name: 'wrapped_phase')
43
+ end
34
44
  end
35
45
 
46
+ let(:wrapped) { wrapped_class.new(wrapped: nil) }
47
+
36
48
  it 'sends the proper notifications' do
37
- expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_started, 'my_phase', rep).ordered
38
- expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_yielded, 'my_phase', rep).ordered
49
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_started, 'MyTestingPhaseClass', rep).ordered
50
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_yielded, 'MyTestingPhaseClass', rep).ordered
39
51
 
40
- expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_started, 'wrapped_phase', rep).ordered
41
- expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_ended, 'wrapped_phase', rep).ordered
52
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_started, 'MyTestingWrappedPhaseClass', rep).ordered
53
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_ended, 'MyTestingWrappedPhaseClass', rep).ordered
42
54
 
43
- expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_resumed, 'my_phase', rep).ordered
44
- expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_ended, 'my_phase', rep).ordered
55
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_resumed, 'MyTestingPhaseClass', rep).ordered
56
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_ended, 'MyTestingPhaseClass', rep).ordered
45
57
 
46
58
  subject
47
59
  end
@@ -435,5 +435,46 @@ describe Nanoc::Int::OutdatednessRules do
435
435
 
436
436
  # …
437
437
  end
438
+
439
+ describe 'UsesAlwaysOutdatedFilter' do
440
+ let(:rule_class) { Nanoc::Int::OutdatednessRules::UsesAlwaysOutdatedFilter }
441
+
442
+ before do
443
+ allow(action_provider).to receive(:memory_for).with(item_rep).and_return(mem)
444
+ end
445
+
446
+ context 'unknown filter' do
447
+ let(:mem) do
448
+ Nanoc::Int::RuleMemory.new(item_rep).tap do |mem|
449
+ mem.add_snapshot(:donkey, '/foo.md')
450
+ mem.add_filter(:asdf, {})
451
+ end
452
+ end
453
+
454
+ it { is_expected.not_to be }
455
+ end
456
+
457
+ context 'known filter, not always outdated' do
458
+ let(:mem) do
459
+ Nanoc::Int::RuleMemory.new(item_rep).tap do |mem|
460
+ mem.add_snapshot(:donkey, '/foo.md')
461
+ mem.add_filter(:erb, {})
462
+ end
463
+ end
464
+
465
+ it { is_expected.not_to be }
466
+ end
467
+
468
+ context 'known filter, always outdated' do
469
+ let(:mem) do
470
+ Nanoc::Int::RuleMemory.new(item_rep).tap do |mem|
471
+ mem.add_snapshot(:donkey, '/foo.md')
472
+ mem.add_filter(:xsl, {})
473
+ end
474
+ end
475
+
476
+ it { is_expected.to be }
477
+ end
478
+ end
438
479
  end
439
480
  end
@@ -1,4 +1,4 @@
1
- describe Nanoc::CLI::Commands::Compile::FileActionPrinter, stdio: true do
1
+ describe Nanoc::CLI::Commands::CompileListeners::FileActionPrinter, stdio: true do
2
2
  let(:listener) { described_class.new(reps: reps) }
3
3
 
4
4
  before { Timecop.freeze(Time.local(2008, 1, 2, 14, 5, 0)) }
@@ -1,4 +1,4 @@
1
- describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
1
+ describe Nanoc::CLI::Commands::CompileListeners::TimingRecorder, stdio: true do
2
2
  let(:listener) { described_class.new(reps: reps) }
3
3
 
4
4
  before { Timecop.freeze(Time.local(2008, 1, 2, 14, 5, 0)) }
@@ -6,6 +6,9 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
6
6
 
7
7
  before { Nanoc::CLI.verbosity = 2 }
8
8
 
9
+ before { listener.start }
10
+ after { listener.stop_safely }
11
+
9
12
  let(:reps) do
10
13
  Nanoc::Int::ItemRepRepo.new.tap do |reps|
11
14
  reps << rep
@@ -20,53 +23,57 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
20
23
  end
21
24
  end
22
25
 
23
- it 'records single from filtering_started to filtering_ended' do
24
- listener.start
26
+ let(:other_rep) do
27
+ Nanoc::Int::ItemRep.new(item, :other).tap do |rep|
28
+ rep.raw_paths = { default: ['/bye.html'] }
29
+ end
30
+ end
25
31
 
32
+ it 'prints filters table' do
26
33
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
27
34
  Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :erb)
28
35
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
29
36
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
37
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 14, 1))
38
+ Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :erb)
39
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 14, 3))
40
+ Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
30
41
 
31
42
  expect { listener.stop }
32
- .to output(/^\s*erb │ 1 1\.00s 1\.00s 1\.00s 1\.00s$/).to_stdout
43
+ .to output(/^\s*erb │ 2 1\.00s 1\.50s 1\.90s 1\.95s 2\.00s 3\.00s$/).to_stdout
33
44
  end
34
45
 
35
- it 'records multiple from filtering_started to filtering_ended' do
36
- listener.start
37
-
46
+ it 'records single from filtering_started to filtering_ended' do
38
47
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
39
48
  Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :erb)
40
49
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
41
50
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
42
- Timecop.freeze(Time.local(2008, 9, 1, 10, 14, 1))
43
- Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :erb)
44
- Timecop.freeze(Time.local(2008, 9, 1, 10, 14, 3))
45
- Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
46
51
 
47
- expect { listener.stop }
48
- .to output(/^\s*erb │ 2 1\.00s 1\.50s 2\.00s 3\.00s$/).to_stdout
52
+ expect(listener.telemetry.summary(:filters).get('erb').min).to eq(1.00)
53
+ expect(listener.telemetry.summary(:filters).get('erb').avg).to eq(1.00)
54
+ expect(listener.telemetry.summary(:filters).get('erb').max).to eq(1.00)
55
+ expect(listener.telemetry.summary(:filters).get('erb').sum).to eq(1.00)
56
+ expect(listener.telemetry.summary(:filters).get('erb').count).to eq(1.00)
49
57
  end
50
58
 
51
- it 'records inner filters in nested filtering_started/filtering_ended' do
52
- listener.start
53
-
59
+ it 'records multiple from filtering_started to filtering_ended' do
54
60
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
55
- Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :outer)
61
+ Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :erb)
56
62
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
57
- Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :inner)
58
- Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 3))
59
- Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :inner)
60
- Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 6))
61
- Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :outer)
63
+ Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
64
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 14, 1))
65
+ Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :erb)
66
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 14, 3))
67
+ Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
62
68
 
63
- expect { listener.stop }
64
- .to output(/^\s*inner │ 1 2\.00s 2\.00s 2\.00s 2\.00s$/).to_stdout
69
+ expect(listener.telemetry.summary(:filters).get('erb').min).to eq(1.00)
70
+ expect(listener.telemetry.summary(:filters).get('erb').avg).to eq(1.50)
71
+ expect(listener.telemetry.summary(:filters).get('erb').max).to eq(2.00)
72
+ expect(listener.telemetry.summary(:filters).get('erb').sum).to eq(3.00)
73
+ expect(listener.telemetry.summary(:filters).get('erb').count).to eq(2.00)
65
74
  end
66
75
 
67
- it 'records outer filters in nested filtering_started/filtering_ended' do
68
- listener.start
69
-
76
+ it 'records filters in nested filtering_started/filtering_ended' do
70
77
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
71
78
  Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :outer)
72
79
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
@@ -76,13 +83,20 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
76
83
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 6))
77
84
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :outer)
78
85
 
79
- expect { listener.stop }
80
- .to output(/^\s*outer │ 1 6\.00s 6\.00s 6\.00s 6\.00s$/).to_stdout
86
+ expect(listener.telemetry.summary(:filters).get('inner').min).to eq(2.00)
87
+ expect(listener.telemetry.summary(:filters).get('inner').avg).to eq(2.00)
88
+ expect(listener.telemetry.summary(:filters).get('inner').max).to eq(2.00)
89
+ expect(listener.telemetry.summary(:filters).get('inner').sum).to eq(2.00)
90
+ expect(listener.telemetry.summary(:filters).get('inner').count).to eq(1.00)
91
+
92
+ expect(listener.telemetry.summary(:filters).get('outer').min).to eq(6.00)
93
+ expect(listener.telemetry.summary(:filters).get('outer').avg).to eq(6.00)
94
+ expect(listener.telemetry.summary(:filters).get('outer').max).to eq(6.00)
95
+ expect(listener.telemetry.summary(:filters).get('outer').sum).to eq(6.00)
96
+ expect(listener.telemetry.summary(:filters).get('outer').count).to eq(1.00)
81
97
  end
82
98
 
83
99
  it 'pauses outer stopwatch when suspended' do
84
- listener.start
85
-
86
100
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
87
101
  Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
88
102
  Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :outer)
@@ -96,13 +110,14 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
96
110
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :inner)
97
111
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :outer)
98
112
 
99
- expect { listener.stop }
100
- .to output(/^\s*outer │ 1 7\.00s 7\.00s 7\.00s 7\.00s$/).to_stdout
113
+ expect(listener.telemetry.summary(:filters).get('outer').min).to eq(7.00)
114
+ expect(listener.telemetry.summary(:filters).get('outer').avg).to eq(7.00)
115
+ expect(listener.telemetry.summary(:filters).get('outer').max).to eq(7.00)
116
+ expect(listener.telemetry.summary(:filters).get('outer').sum).to eq(7.00)
117
+ expect(listener.telemetry.summary(:filters).get('outer').count).to eq(1.00)
101
118
  end
102
119
 
103
120
  it 'records single from filtering_started over compilation_{suspended,started} to filtering_ended' do
104
- listener.start
105
-
106
121
  Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
107
122
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
108
123
  Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :erb)
@@ -113,25 +128,27 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
113
128
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 7))
114
129
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
115
130
 
116
- expect { listener.stop }
117
- .to output(/^\s*erb │ 1 5\.00s 5\.00s 5\.00s 5\.00s$/).to_stdout
131
+ expect(listener.telemetry.summary(:filters).get('erb').min).to eq(5.00)
132
+ expect(listener.telemetry.summary(:filters).get('erb').avg).to eq(5.00)
133
+ expect(listener.telemetry.summary(:filters).get('erb').max).to eq(5.00)
134
+ expect(listener.telemetry.summary(:filters).get('erb').sum).to eq(5.00)
135
+ expect(listener.telemetry.summary(:filters).get('erb').count).to eq(1.00)
118
136
  end
119
137
 
120
138
  it 'records single phase start+stop' do
121
- listener.start
122
-
123
139
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
124
140
  Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
125
141
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
126
142
  Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
127
143
 
128
- expect { listener.stop }
129
- .to output(/^\s*donkey │ 1 1\.00s 1\.00s 1\.00s 1\.00s$/).to_stdout
144
+ expect(listener.telemetry.summary(:phases).get('donkey').min).to eq(1.00)
145
+ expect(listener.telemetry.summary(:phases).get('donkey').avg).to eq(1.00)
146
+ expect(listener.telemetry.summary(:phases).get('donkey').max).to eq(1.00)
147
+ expect(listener.telemetry.summary(:phases).get('donkey').sum).to eq(1.00)
148
+ expect(listener.telemetry.summary(:phases).get('donkey').count).to eq(1.00)
130
149
  end
131
150
 
132
151
  it 'records multiple phase start+stop' do
133
- listener.start
134
-
135
152
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
136
153
  Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
137
154
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
@@ -141,13 +158,14 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
141
158
  Timecop.freeze(Time.local(2008, 9, 1, 11, 6, 2))
142
159
  Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
143
160
 
144
- expect { listener.stop }
145
- .to output(/^\s*donkey │ 2 1\.00s 1\.50s 2\.00s 3\.00s$/).to_stdout
161
+ expect(listener.telemetry.summary(:phases).get('donkey').min).to eq(1.00)
162
+ expect(listener.telemetry.summary(:phases).get('donkey').avg).to eq(1.50)
163
+ expect(listener.telemetry.summary(:phases).get('donkey').max).to eq(2.00)
164
+ expect(listener.telemetry.summary(:phases).get('donkey').sum).to eq(3.00)
165
+ expect(listener.telemetry.summary(:phases).get('donkey').count).to eq(2.00)
146
166
  end
147
167
 
148
168
  it 'records single phase start+yield+resume+stop' do
149
- listener.start
150
-
151
169
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
152
170
  Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
153
171
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
@@ -157,13 +175,14 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
157
175
  Timecop.freeze(Time.local(2008, 9, 1, 11, 6, 2))
158
176
  Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
159
177
 
160
- expect { listener.stop }
161
- .to output(/^\s*donkey │ 1 3\.00s 3\.00s 3\.00s 3\.00s$/).to_stdout
178
+ expect(listener.telemetry.summary(:phases).get('donkey').min).to eq(3.00)
179
+ expect(listener.telemetry.summary(:phases).get('donkey').avg).to eq(3.00)
180
+ expect(listener.telemetry.summary(:phases).get('donkey').max).to eq(3.00)
181
+ expect(listener.telemetry.summary(:phases).get('donkey').sum).to eq(3.00)
182
+ expect(listener.telemetry.summary(:phases).get('donkey').count).to eq(1.00)
162
183
  end
163
184
 
164
185
  it 'records single phase start+yield+abort+start+stop' do
165
- listener.start
166
-
167
186
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
168
187
  Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
169
188
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
@@ -175,13 +194,24 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
175
194
  Timecop.freeze(Time.local(2008, 9, 1, 12, 7, 5))
176
195
  Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
177
196
 
178
- expect { listener.stop }
179
- .to output(/^\s*donkey │ 2 1\.00s 2\.00s 3\.00s 4\.00s$/).to_stdout
197
+ expect(listener.telemetry.summary(:phases).get('donkey').min).to eq(1.00)
198
+ expect(listener.telemetry.summary(:phases).get('donkey').avg).to eq(2.00)
199
+ expect(listener.telemetry.summary(:phases).get('donkey').max).to eq(3.00)
200
+ expect(listener.telemetry.summary(:phases).get('donkey').sum).to eq(4.00)
201
+ expect(listener.telemetry.summary(:phases).get('donkey').count).to eq(2.00)
180
202
  end
181
203
 
182
204
  it 'records stage duration' do
183
- listener.start
205
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
206
+ Nanoc::Int::NotificationCenter.post(:stage_started, 'donkey', rep)
207
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
208
+ Nanoc::Int::NotificationCenter.post(:stage_ended, 'donkey', rep)
184
209
 
210
+ expect(listener.telemetry.summary(:stages).get('donkey').sum).to eq(1.00)
211
+ expect(listener.telemetry.summary(:stages).get('donkey').count).to eq(1.00)
212
+ end
213
+
214
+ it 'prints stage durations' do
185
215
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
186
216
  Nanoc::Int::NotificationCenter.post(:stage_started, 'donkey', rep)
187
217
  Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
@@ -190,4 +220,44 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
190
220
  expect { listener.stop }
191
221
  .to output(/^\s*donkey │ 1\.00s$/).to_stdout
192
222
  end
223
+
224
+ it 'prints out outdatedness rule durations' do
225
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
226
+ Nanoc::Int::NotificationCenter.post(:outdatedness_rule_started, Nanoc::Int::OutdatednessRules::CodeSnippetsModified, rep)
227
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
228
+ Nanoc::Int::NotificationCenter.post(:outdatedness_rule_ended, Nanoc::Int::OutdatednessRules::CodeSnippetsModified, rep)
229
+
230
+ expect { listener.stop }
231
+ .to output(/^\s*CodeSnippetsModified │ 1 1\.00s 1\.00s 1\.00s 1\.00s 1\.00s 1\.00s$/).to_stdout
232
+ end
233
+
234
+ it 'records single outdatedness rule duration' do
235
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
236
+ Nanoc::Int::NotificationCenter.post(:outdatedness_rule_started, Nanoc::Int::OutdatednessRules::CodeSnippetsModified, rep)
237
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
238
+ Nanoc::Int::NotificationCenter.post(:outdatedness_rule_ended, Nanoc::Int::OutdatednessRules::CodeSnippetsModified, rep)
239
+
240
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').min).to eq(1.00)
241
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').avg).to eq(1.00)
242
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').max).to eq(1.00)
243
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').sum).to eq(1.00)
244
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').count).to eq(1.00)
245
+ end
246
+
247
+ it 'records multiple outdatedness rule duration' do
248
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
249
+ Nanoc::Int::NotificationCenter.post(:outdatedness_rule_started, Nanoc::Int::OutdatednessRules::CodeSnippetsModified, rep)
250
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
251
+ Nanoc::Int::NotificationCenter.post(:outdatedness_rule_ended, Nanoc::Int::OutdatednessRules::CodeSnippetsModified, rep)
252
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 6, 0))
253
+ Nanoc::Int::NotificationCenter.post(:outdatedness_rule_started, Nanoc::Int::OutdatednessRules::CodeSnippetsModified, other_rep)
254
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 6, 3))
255
+ Nanoc::Int::NotificationCenter.post(:outdatedness_rule_ended, Nanoc::Int::OutdatednessRules::CodeSnippetsModified, other_rep)
256
+
257
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').min).to eq(1.00)
258
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').avg).to eq(2.00)
259
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').max).to eq(3.00)
260
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').sum).to eq(4.00)
261
+ expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').count).to eq(2.00)
262
+ end
193
263
  end