benchmark-memory 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +16 -0
- data/CONTRIBUTING.md +50 -0
- data/LICENSE.md +21 -0
- data/README.md +183 -0
- data/Rakefile +29 -0
- data/benchmark-memory.gemspec +24 -0
- data/lib/benchmark-memory.rb +2 -0
- data/lib/benchmark/memory.rb +34 -0
- data/lib/benchmark/memory/errors.rb +7 -0
- data/lib/benchmark/memory/held_results.rb +110 -0
- data/lib/benchmark/memory/held_results/entry_serializer.rb +35 -0
- data/lib/benchmark/memory/held_results/measurement_serializer.rb +37 -0
- data/lib/benchmark/memory/held_results/metric_serializer.rb +37 -0
- data/lib/benchmark/memory/held_results/serializer.rb +64 -0
- data/lib/benchmark/memory/helpers.rb +46 -0
- data/lib/benchmark/memory/job.rb +135 -0
- data/lib/benchmark/memory/job/io_output.rb +50 -0
- data/lib/benchmark/memory/job/io_output/comparison_formatter.rb +69 -0
- data/lib/benchmark/memory/job/io_output/entry_formatter.rb +39 -0
- data/lib/benchmark/memory/job/io_output/metric_formatter.rb +34 -0
- data/lib/benchmark/memory/job/null_output.rb +26 -0
- data/lib/benchmark/memory/job/task.rb +50 -0
- data/lib/benchmark/memory/measurement.rb +80 -0
- data/lib/benchmark/memory/measurement/metric.rb +41 -0
- data/lib/benchmark/memory/report.rb +45 -0
- data/lib/benchmark/memory/report/comparison.rb +25 -0
- data/lib/benchmark/memory/report/entry.rb +32 -0
- data/lib/benchmark/memory/version.rb +5 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea91842c5ad96cfe3dc3c101bf3d7179b6b1871f
|
4
|
+
data.tar.gz: 75947cf78b65b8286bceec66bf4ace28403cc5ac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 23a9674497bab9bf07ecaaf07c0f51cdf880223a924de621806afa8f9f62fdca4cf0db59913b9f2372d77a8d849b73f297d106a0a80f9c9c0cd0c15878e5503b
|
7
|
+
data.tar.gz: 8eab918622dbb1e0dfbea807fa39c4404489510a90f3d37632dad8bbb6b0f8fad00fea34ce30a684f3f3b4ec74e4a6d00cadd30b742bf826a65631a5907f5c5d
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning 2.0.0][semver]. Any violations of this scheme are considered to be bugs.
|
4
|
+
|
5
|
+
[semver]: http://semver.org/spec/v2.0.0.html
|
6
|
+
|
7
|
+
## [0.1.0] - 2016-05-18
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Main `Benchmark.memory` method.
|
12
|
+
- Holding results between invocations for measuring implementations on different versions of Ruby or different versions of libraries.
|
13
|
+
- Quiet mode, with no command line output.
|
14
|
+
|
15
|
+
[0.1.0]: https://github.com/michaelherold/benchmark-memory/tree/v0.1.0
|
16
|
+
[unreleased]: https://github.com/michaelherold/benchmark-memory/compare/v0.1.0...HEAD
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), **everyone** is encouraged to help improve this project. Here are some ways *you* can contribute:
|
4
|
+
|
5
|
+
* Use alpha, beta, and pre-release versions.
|
6
|
+
* Report bugs.
|
7
|
+
* Suggest new features.
|
8
|
+
* Write or edit documentation.
|
9
|
+
* Write specifications.
|
10
|
+
* Write code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace).
|
11
|
+
* Refactor code.
|
12
|
+
* Fix [issues][].
|
13
|
+
* Review patches.
|
14
|
+
|
15
|
+
[issues]: https://github.com/michaelherold/benchmark-memory/issues
|
16
|
+
|
17
|
+
## Submitting an Issue
|
18
|
+
|
19
|
+
We use the [GitHub issue tracker][issues] to track bugs and features. Before submitting a bug report or feature request, check to make sure it hasn't already been submitted.
|
20
|
+
|
21
|
+
When submitting a bug report, please include a [Gist](https://gist.github.com) that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system.
|
22
|
+
|
23
|
+
Ideally, a bug report should include a pull request with failing specs.
|
24
|
+
|
25
|
+
## Submitting a Pull Request
|
26
|
+
|
27
|
+
1. [Fork the repository](http://help.github.com/fork-a-repo/).
|
28
|
+
2. [Create a topic branch](http://learn.github.com/p/branching.html).
|
29
|
+
3. Add specs for your unimplemented feature or bug fix.
|
30
|
+
4. Run `bundle exec rake spec`. If your specs pass, return to step 3.
|
31
|
+
5. Implement your feature or bug fix.
|
32
|
+
6. Run `bundle exec rake`. If your specs or any of the linters fail, return to step 5.
|
33
|
+
7. Open `coverage/index.html`. If your changes are not completely covered by your tests, return to step 3.
|
34
|
+
8. Add documentation for your feature or bug fix.
|
35
|
+
9. Run `bundle exec inch`. If your changes are below a B in documentation, go back to step 8.
|
36
|
+
10. Commit and push your changes.
|
37
|
+
11. [Submit a pull request](http://help.github.com/send-pull-requests/).
|
38
|
+
|
39
|
+
## Tools to help you succeed
|
40
|
+
|
41
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
42
|
+
|
43
|
+
When writing code, you can use the helper application [Guard][guard] to automatically run tests and coverage tools whenever you modify and save a file. This helps to eliminate the tedium of running tests manually and reduces the chance that you will accidentally forget to run the tests. To use Guard, run `bundle exec guard`.
|
44
|
+
|
45
|
+
Before committing code, run `rake` to check that the code conforms to the style guidelines of the project, that all of the tests are green (if you're writing a feature; if you're only submitting a failing test, then it does not have to pass!), and that the changes are sufficiently documented.
|
46
|
+
|
47
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org][rubygems].
|
48
|
+
|
49
|
+
[guard]: http://guardgem.org
|
50
|
+
[rubygems]: https://rubygems.org
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Michael Herold
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
# benchmark-memory
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/michaelherold/benchmark-memory.svg)][travis]
|
4
|
+
[![Code Climate](https://codeclimate.com/github/michaelherold/benchmark-memory/badges/gpa.svg)][codeclimate]
|
5
|
+
[![Inline docs](http://inch-ci.org/github/michaelherold/benchmark-memory.svg?branch=master)][inch]
|
6
|
+
|
7
|
+
[codeclimate]: https://codeclimate.com/github/michaelherold/benchmark-memory
|
8
|
+
[inch]: http://inch-ci.org/github/michaelherold/benchmark-memory
|
9
|
+
[travis]: https://travis-ci.org/michaelherold/benchmark-memory
|
10
|
+
|
11
|
+
benchmark-memory is a tool that helps you to benchmark the memory usage of different pieces of code. It leverages the power of [memory_profiler] to give you a metric of the total amount of memory allocated and retained by a block, as well as the number of objects and strings allocated and retained.
|
12
|
+
|
13
|
+
[memory_profiler]: https://github.com/SamSaffron/memory_profiler
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem "benchmark-memory"
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install benchmark-memory
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
Following the examples of the built-in Benchmark and Evan Phoenix's [benchmark-ips], the most common way of using benchmark-memory is through the `Benchmark.memory` wrapper. An example might look like this:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require "benchmark/memory"
|
37
|
+
|
38
|
+
# First method under test
|
39
|
+
def allocate_string
|
40
|
+
"this string was dynamically allocated"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Second method under test
|
44
|
+
def give_frozen_string
|
45
|
+
"this string is frozen".freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
Benchmark.memory do |x|
|
49
|
+
x.report("dynamic allocation") { allocate_string }
|
50
|
+
x.report("frozen string") { give_frozen_string }
|
51
|
+
|
52
|
+
x.compare!
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
This example tests two methods that are defined inline. Note that you don't have to define them inline; you can just as easily use a method that you require before the benchmark or anything else that you can place in a block.
|
57
|
+
|
58
|
+
When you run this example, you see the difference between the two reports:
|
59
|
+
|
60
|
+
```txt
|
61
|
+
Calculating -------------------------------------
|
62
|
+
dynamic allocation 40.000 memsize ( 0.000 retained)
|
63
|
+
1.000 objects ( 0.000 retained)
|
64
|
+
1.000 strings ( 0.000 retained)
|
65
|
+
frozen string 0.000 memsize ( 0.000 retained)
|
66
|
+
0.000 objects ( 0.000 retained)
|
67
|
+
0.000 strings ( 0.000 retained)
|
68
|
+
|
69
|
+
Comparison:
|
70
|
+
frozen string: 0 allocated
|
71
|
+
dynamic allocation: 40 allocated - Infx more
|
72
|
+
```
|
73
|
+
|
74
|
+
Reading this output shows that the "dynamic allocation" example allocates one string that is not retained outside the scope of the block. The "frozen string" example, however, does not allocate anything because it reuses the frozen string that was created during the method definition.
|
75
|
+
|
76
|
+
[benchmark-ips]: https://github.com/evanphx/benchmark-ips
|
77
|
+
|
78
|
+
## Options
|
79
|
+
|
80
|
+
There are several options available when running a memory benchmark.
|
81
|
+
|
82
|
+
### Suppress all output (Quiet Mode)
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
Benchmark.memory(:quiet => true)
|
86
|
+
```
|
87
|
+
|
88
|
+
Passing a `:quiet` flag to the `Benchmark.memory` method suppresses the output of the benchmark. You might find this useful if you want to run a benchmark as part of your test suite, where outputting to `STDOUT` would be disruptive.
|
89
|
+
|
90
|
+
### Enable comparison
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
Benchmark.memory do |x|
|
94
|
+
x.compare!
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
Calling `#compare!` on the job within the setup block of `Benchmark.memory` enables the output of the comparison section of the benchmark. Without it, this section is suppressed and you only get the raw numbers output during calculation.
|
99
|
+
|
100
|
+
### Hold results between invocations
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
Benchmark.memory do |x|
|
104
|
+
x.hold!("benchmark_results.json")
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
Often when you want to benchmark something, you compare two implementations of the same method. This is cumbersome because you have to keep two implementations side-by-side and call them in the same manner. Alternatively, you may want to compare how a method performs on two different versions of Ruby. To make both of these scenarios easier, you can enable "holding" on the benchmark.
|
109
|
+
|
110
|
+
By calling `#hold!` on the benchmark, you enable the benchmark to write to the given file to store its results in a file that can be read in between invocations of your benchmark.
|
111
|
+
|
112
|
+
For example, imagine that you have a library that exposes a method called `Statistics.calculate_monthly_recurring_revenue` that you want to optimize for memory usage because it keeps causing your worker server to run out of memory. You make some changes to the method and commit them to an `optimize-memory` branch in Git.
|
113
|
+
|
114
|
+
To test the two implementations, you could then write this benchmark:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
require "benchmark/memory"
|
118
|
+
|
119
|
+
require "stats" # require your library file here
|
120
|
+
data = [] # set up the data that it will call here
|
121
|
+
|
122
|
+
Benchmark.memory do |x|
|
123
|
+
x.report("original") { Stats.monthly_recurring_revenue(data) }
|
124
|
+
x.report("optimized") { Stats.monthly_recurring_revenue(data) }
|
125
|
+
|
126
|
+
x.compare!
|
127
|
+
x.hold("bm_recurring_revenue.json")
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
Note that the method calls are the same for both tests and that we have enabled result holding in the "bm_recurring_revenue.json" file.
|
132
|
+
|
133
|
+
You could then run the following (assuming you saved your benchmark as `benchmark_mrr.rb`:
|
134
|
+
|
135
|
+
```sh
|
136
|
+
$ git checkout master
|
137
|
+
$ ruby benchmark_mrr.rb
|
138
|
+
$ git checkout optimize-memory
|
139
|
+
$ ruby benchmark_mrr.rb
|
140
|
+
```
|
141
|
+
|
142
|
+
The first invocation of `ruby benchmark_mrr.rb` runs the benchmark in the "original" entry using your code in your `master` Git branch. The second invocation runs the benchmark in the "optimized" entry using the code in your `optimize-memory` Git branch. These two results are collated and then compared to show you the difference between the two.
|
143
|
+
|
144
|
+
When enabling holding, the benchmark writes to the file passed into the `#hold!` method. After you run all of the entries in the benchmark, the benchmark automatically cleans up its log by deleting the file.
|
145
|
+
|
146
|
+
## Supported Ruby Versions
|
147
|
+
|
148
|
+
This library aims to support and is [tested against][travis] the following Ruby versions:
|
149
|
+
|
150
|
+
* Ruby 2.1
|
151
|
+
* Ruby 2.2
|
152
|
+
* Ruby 2.3
|
153
|
+
|
154
|
+
If something doesn't work on one of these versions, it's a bug.
|
155
|
+
|
156
|
+
This library may inadvertently work (or seem to work) on other Ruby versions, however, support will only be provided for the versions listed above.
|
157
|
+
|
158
|
+
If you would like this library to support another Ruby version or implementation, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped.
|
159
|
+
|
160
|
+
## Versioning
|
161
|
+
|
162
|
+
This library aims to adhere to [Semantic Versioning 2.0.0][semver]. Violations of this scheme should be reported as bugs. Specifically, if a minor or patch version is released that breaks backward compatibility, that version should be immediately yanked and/or a new version should be immediately released that restores compatibility. Breaking changes to the public API will only be introduced with new major versions. As a result of this policy, you can (and should) specify a dependency on this gem using the [Pessimistic Version Constraint][pessimistic] with two digits of precision. For example:
|
163
|
+
|
164
|
+
spec.add_dependency "benchmark-memory", "~> 0.1"
|
165
|
+
|
166
|
+
[pessimistic]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
|
167
|
+
[semver]: http://semver.org/spec/v2.0.0.html
|
168
|
+
|
169
|
+
## Acknowledgments
|
170
|
+
|
171
|
+
This library wouldn't be possible without two projects and the people behind them:
|
172
|
+
|
173
|
+
* Sam Saffron's [memory_profiler] does all of the measurement of the memory allocation and retention in the benchmarks.
|
174
|
+
* I based much of the code around Evan Phoenix's [benchmark-ips] project, since it has a clean base from which to work and a logical organization. I also wanted to go for feature- and DSL-parity with it because I really like the way it works.
|
175
|
+
|
176
|
+
[benchmark-ips]: https://github.com/evanphx/benchmark-ips
|
177
|
+
[memory_profiler]: https://github.com/SamSaffron/memory_profiler
|
178
|
+
|
179
|
+
## License
|
180
|
+
|
181
|
+
The gem is available as open source under the terms of the [MIT License][license].
|
182
|
+
|
183
|
+
[license]: http://opensource.org/licenses/MIT.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "bundler"
|
2
|
+
Bundler.setup
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
require "inch/rake"
|
6
|
+
Inch::Rake::Suggest.new(:inch)
|
7
|
+
|
8
|
+
require "rspec/core/rake_task"
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
|
11
|
+
require "rubocop/rake_task"
|
12
|
+
RuboCop::RakeTask.new(:rubocop)
|
13
|
+
|
14
|
+
require "yard/rake/yardoc_task"
|
15
|
+
YARD::Rake::YardocTask.new(:yard)
|
16
|
+
|
17
|
+
task :mutant do
|
18
|
+
command = [
|
19
|
+
"bundle exec mutant",
|
20
|
+
"--include lib",
|
21
|
+
"--require interactor-contracts",
|
22
|
+
"--use rspec",
|
23
|
+
"Interactor::Contracts*",
|
24
|
+
].join(" ")
|
25
|
+
|
26
|
+
system command
|
27
|
+
end
|
28
|
+
|
29
|
+
task :default => [:spec, :rubocop, :yard, :inch]
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path("../lib/benchmark/memory/version", __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "benchmark-memory"
|
7
|
+
spec.version = Benchmark::Memory::VERSION
|
8
|
+
spec.authors = ["Michael Herold"]
|
9
|
+
spec.email = ["michael.j.herold@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Benchmark-style memory profiling for Ruby 2.1+"
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/michaelherold/benchmark-memory"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = %w(CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md Rakefile)
|
17
|
+
spec.files += %w(benchmark-memory.gemspec)
|
18
|
+
spec.files += Dir["lib/**/*.rb"]
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "memory_profiler", "~> 0.9"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "benchmark/memory/errors"
|
2
|
+
require "benchmark/memory/job"
|
3
|
+
require "benchmark/memory/version"
|
4
|
+
|
5
|
+
# Performance benchmarking library
|
6
|
+
module Benchmark
|
7
|
+
# Benchmark memory usage in code to benchmark different approaches.
|
8
|
+
# @see https://github.com/michaelherold/benchmark-memory
|
9
|
+
module Memory
|
10
|
+
# Measure memory usage in report blocks.
|
11
|
+
#
|
12
|
+
# @param quiet [Boolean] A flag to toggle benchmark output.
|
13
|
+
#
|
14
|
+
# @return [Report]
|
15
|
+
def memory(quiet: false)
|
16
|
+
unless block_given?
|
17
|
+
fail(
|
18
|
+
ConfigurationError,
|
19
|
+
"You did not give a test block to your call to `Benchmark.memory`"
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
job = Job.new(:quiet => quiet)
|
24
|
+
|
25
|
+
yield job
|
26
|
+
|
27
|
+
job.run
|
28
|
+
job.run_comparison
|
29
|
+
job.full_report
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
extend Benchmark::Memory
|
34
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "benchmark/memory/held_results/entry_serializer"
|
3
|
+
|
4
|
+
module Benchmark
|
5
|
+
module Memory
|
6
|
+
# Collate results that should be held until the next run.
|
7
|
+
class HeldResults
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
# Instantiate a new set of held results on a path.
|
11
|
+
#
|
12
|
+
# @param path [String, IO] The path to write held results to.
|
13
|
+
def initialize(path = nil)
|
14
|
+
@path = path
|
15
|
+
@results = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String, IO] The path to write held results to.
|
19
|
+
attr_accessor :path
|
20
|
+
|
21
|
+
# @return [Hash{String => Measurement}] Held results from previous runs.
|
22
|
+
attr_reader :results
|
23
|
+
|
24
|
+
# Allow Hash-like access to the results without asking for them.
|
25
|
+
def_delegator :@results, :[]
|
26
|
+
|
27
|
+
# Add a result to the held results.
|
28
|
+
#
|
29
|
+
# @param entry [Report::Entry] The entry to hold.
|
30
|
+
#
|
31
|
+
# @return [void]
|
32
|
+
def add_result(entry)
|
33
|
+
with_hold_file("a") do |file|
|
34
|
+
file.write EntrySerializer.new(entry)
|
35
|
+
file.write "\n"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Check whether any results have been stored.
|
40
|
+
#
|
41
|
+
# @return [Boolean]
|
42
|
+
def any?
|
43
|
+
if @path.is_a?(String)
|
44
|
+
File.exist?(@path)
|
45
|
+
else
|
46
|
+
@path.size > 0 # rubocop:disable Style/ZeroLengthPredicate
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Clean up the results after all results have been collated.
|
51
|
+
#
|
52
|
+
# @return [void]
|
53
|
+
def cleanup
|
54
|
+
if @path.is_a?(String) && File.exist?(@path)
|
55
|
+
File.delete(@path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check whether to hold results.
|
60
|
+
#
|
61
|
+
# @return [Boolean]
|
62
|
+
def holding?
|
63
|
+
!!@path
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check whether an entry has been added to the results.
|
67
|
+
#
|
68
|
+
# @param entry [#label] The entry to check.
|
69
|
+
#
|
70
|
+
# @return [Boolean]
|
71
|
+
def include?(entry)
|
72
|
+
holding? && any? && results.key?(entry.label)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Load results from the serialized output.
|
76
|
+
#
|
77
|
+
# @return [void]
|
78
|
+
def load
|
79
|
+
return unless holding? && any?
|
80
|
+
|
81
|
+
results = with_hold_file do |file|
|
82
|
+
file.map { |line| EntrySerializer.load(line) }
|
83
|
+
end
|
84
|
+
@results = Hash[results.map do |result|
|
85
|
+
[result.label, result.measurement]
|
86
|
+
end]
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Execute a block on the hold file.
|
92
|
+
#
|
93
|
+
# @param access_mode [String] The mode to use when opening the file.
|
94
|
+
# @param _block [Proc] The block to execute on each line of the file.
|
95
|
+
#
|
96
|
+
# @return [void]
|
97
|
+
def with_hold_file(access_mode = "r", &_block)
|
98
|
+
return unless @path
|
99
|
+
|
100
|
+
if @path.is_a?(String)
|
101
|
+
File.open(@path, access_mode) do |f|
|
102
|
+
yield f
|
103
|
+
end
|
104
|
+
else
|
105
|
+
yield @path
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|