nanoc 4.7.0 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
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