scientist 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 768eab788fd168ba799412e106ec5b386a15c6f020cf6590873245262049481f
4
- data.tar.gz: 2dffdf57ed21e9684024cc09920721faf1525d129c4b985957e04b657451942b
3
+ metadata.gz: b2c73b090777600d31576b815584cba175ba2061ef223b5ec6775038b03828ee
4
+ data.tar.gz: 0336cebef463c2c7b92246d7124a395f7351a75452071595ec3c7edac63050f8
5
5
  SHA512:
6
- metadata.gz: 871dbc71c6795365321f2b4265c6c121a11a11dd6a440b3779eb93c4a30737419f573005d100fa8920bcdfa5f77730acf0906f19aaac58d0a4be42c0703f6228
7
- data.tar.gz: 52471d998257836b26e3b61a181426e3bab90a12eeaa73999e115edb95116f3730a0547588293ca26676b021eb977bda10cb1c07fcaf5d70d95c36abc0c65742
6
+ metadata.gz: e97b6ba1d1f6a07d518b8d86b1b60dbfc0a978e3ef77e95490f733e290fcdef7a42c73728b7f17657a379706bfbfb5ff2d8dbc24334fdb0f7847069c46c5629f
7
+ data.tar.gz: 3c2b414b032e96eebf4fe476078dc170384efaf67622a32f24db44a52b1dec1c0c4da2706151d06a9dbd3e30da1655ad06d4add6db0af25dd9d85f540014452a
@@ -3,11 +3,10 @@ language: ruby
3
3
  cache: bundler
4
4
  script: script/test
5
5
  rvm:
6
- - 2.1.10
7
- - 2.2.9
8
- - 2.3.7
9
- - 2.4.4
10
- - 2.5.1
6
+ - 2.3.8
7
+ - 2.4.5
8
+ - 2.5.3
9
+ - 2.6.1
11
10
  before_install: gem install bundler
12
11
  addons:
13
12
  apt:
data/README.md CHANGED
@@ -206,6 +206,8 @@ class MyExperiment
206
206
  end
207
207
  ```
208
208
 
209
+ Note that the `#clean` method will discard the previous cleaner block if you call it again. If for some reason you need to access the currently configured cleaner block, `Scientist::Experiment#cleaner` will return the block without further ado. _(This probably won't come up in normal usage, but comes in handy if you're writing, say, a custom experiment runner that provides default cleaners.)_
210
+
209
211
  ### Ignoring mismatches
210
212
 
211
213
  During the early stages of an experiment, it's possible that some of your code will always generate a mismatch for reasons you know and understand but haven't yet fixed. Instead of these known cases always showing up as mismatches in your metrics or analysis, you can tell an experiment whether or not to ignore a mismatch using the `ignore` method. You may include more than one block if needed:
@@ -491,6 +493,22 @@ science "various-ways", run: "first-way" do |e|
491
493
  end
492
494
  ```
493
495
 
496
+ #### Providing fake timing data
497
+
498
+ If you're writing tests that depend on specific timing values, you can provide canned durations using the `fabricate_durations_for_testing_purposes` method, and Scientist will report these in `Scientist::Observation#duration` instead of the actual execution times.
499
+
500
+ ```ruby
501
+ science "absolutely-nothing-suspicious-happening-here" do |e|
502
+ e.use { ... } # "control"
503
+ e.try { ... } # "candidate"
504
+ e.fabricate_durations_for_testing_purposes( "control" => 1.0, "candidate" => 0.5 )
505
+ end
506
+ ```
507
+
508
+ `fabricate_durations_for_testing_purposes` takes a Hash of duration values, keyed by behavior names. (By default, Scientist uses `"control"` and `"candidate"`, but if you override these as shown in [Trying more than one thing](#trying-more-than-one-thing) or [No control, just candidates](#no-control-just-candidates), use matching names here.) If a name is not provided, the actual execution time will be reported instead.
509
+
510
+ _Like `Scientist::Experiment#cleaner`, this probably won't come up in normal usage. It's here to make it easier to test code that extends Scientist._
511
+
494
512
  ### Without including Scientist
495
513
 
496
514
  If you need to use Scientist in a place where you aren't able to include the Scientist module, you can call `Scientist.run`:
@@ -504,12 +522,12 @@ end
504
522
 
505
523
  ## Hacking
506
524
 
507
- Be on a Unixy box. Make sure a modern Bundler is available. `script/test` runs the unit tests. All development dependencies are installed automatically. Scientist requires Ruby 2.1 or newer.
525
+ Be on a Unixy box. Make sure a modern Bundler is available. `script/test` runs the unit tests. All development dependencies are installed automatically. Scientist requires Ruby 2.3 or newer.
508
526
 
509
527
  ## Alternatives
510
528
 
511
529
  - [daylerees/scientist](https://github.com/daylerees/scientist) (PHP)
512
- - [github/scientist.net](https://github.com/github/scientist.net) (.NET)
530
+ - [scientistproject/scientist.net](https://github.com/scientistproject/Scientist.net) (.NET)
513
531
  - [joealcorn/laboratory](https://github.com/joealcorn/laboratory) (Python)
514
532
  - [rawls238/Scientist4J](https://github.com/rawls238/Scientist4J) (Java)
515
533
  - [tomiaijo/scientist](https://github.com/tomiaijo/scientist) (C++)
@@ -523,6 +541,7 @@ Be on a Unixy box. Make sure a modern Bundler is available. `script/test` runs t
523
541
  - [calavera/go-scientist](https://github.com/calavera/go-scientist) (Go)
524
542
  - [jelmersnoeck/experiment](https://github.com/jelmersnoeck/experiment) (Go)
525
543
  - [spoptchev/scientist](https://github.com/spoptchev/scientist) (Kotlin / Java)
544
+ - [junkpiano/scientist](https://github.com/junkpiano/scientist) (Swift)
526
545
 
527
546
 
528
547
  ## Maintainers
@@ -1,5 +1,12 @@
1
1
  # Changes
2
2
 
3
+ ## v1.3.0 (2 April 2019)
4
+
5
+ - New: Drop support for ruby <2.3
6
+ - Fix: Build new strings instead of modifying frozen ones
7
+ - New: Add an accessor for the configured clean block
8
+ - New: Add a hook to use fabricated durations instead of actual timing data.
9
+
3
10
  ## v1.2.0 (5 July 2018)
4
11
 
5
12
  - New: Use monotonic clock for duration calculations
@@ -41,10 +41,10 @@ module Scientist::Experiment
41
41
  def format_observation(observation)
42
42
  observation.name + ":\n" +
43
43
  if observation.raised?
44
- observation.exception.inspect.prepend(" ") + "\n" +
45
- observation.exception.backtrace.map { |line| line.prepend(" ") }.join("\n")
44
+ lines = observation.exception.backtrace.map { |line| " #{line}" }.join("\n")
45
+ " #{observation.exception.inspect}" + "\n" + lines
46
46
  else
47
- observation.cleaned_value.inspect.prepend(" ")
47
+ " #{observation.cleaned_value.inspect}"
48
48
  end
49
49
  end
50
50
  end
@@ -96,6 +96,13 @@ module Scientist::Experiment
96
96
  @_scientist_cleaner = block
97
97
  end
98
98
 
99
+ # Accessor for the clean block, if one is available.
100
+ #
101
+ # Returns the configured block, or nil.
102
+ def cleaner
103
+ @_scientist_cleaner
104
+ end
105
+
99
106
  # Internal: Clean a value with the configured clean block, or return the value
100
107
  # if no clean block is configured.
101
108
  #
@@ -213,7 +220,8 @@ module Scientist::Experiment
213
220
 
214
221
  behaviors.keys.shuffle.each do |key|
215
222
  block = behaviors[key]
216
- observations << Scientist::Observation.new(key, self, &block)
223
+ fabricated_duration = @_scientist_fabricated_durations && @_scientist_fabricated_durations[key]
224
+ observations << Scientist::Observation.new(key, self, fabricated_duration: fabricated_duration, &block)
217
225
  end
218
226
 
219
227
  control = observations.detect { |o| o.name == name }
@@ -290,4 +298,10 @@ module Scientist::Experiment
290
298
  !!raise_on_mismatches
291
299
  end
292
300
  end
301
+
302
+ # Provide predefined durations to use instead of actual timing data.
303
+ # This is here solely as a convenience for developers of libraries that extend Scientist.
304
+ def fabricate_durations_for_testing_purposes(fabricated_durations = {})
305
+ @_scientist_fabricated_durations = fabricated_durations
306
+ end
293
307
  end
@@ -23,19 +23,20 @@ class Scientist::Observation
23
23
  # The Float seconds elapsed.
24
24
  attr_reader :duration
25
25
 
26
- def initialize(name, experiment, &block)
26
+ def initialize(name, experiment, fabricated_duration: nil, &block)
27
27
  @name = name
28
28
  @experiment = experiment
29
29
  @now = Time.now
30
30
 
31
- starting = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
31
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) unless fabricated_duration
32
32
  begin
33
33
  @value = block.call
34
34
  rescue *RESCUES => e
35
35
  @exception = e
36
36
  end
37
37
 
38
- @duration = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) - starting
38
+ @duration = fabricated_duration ||
39
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) - starting
39
40
 
40
41
  freeze
41
42
  end
@@ -1,3 +1,3 @@
1
1
  module Scientist
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
10
10
  gem.homepage = "https://github.com/github/scientist"
11
11
  gem.license = "MIT"
12
12
 
13
- gem.required_ruby_version = '>= 2.1'
13
+ gem.required_ruby_version = '>= 2.3'
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
16
16
  gem.executables = []
@@ -33,7 +33,6 @@ git fetch -t origin
33
33
  }
34
34
 
35
35
  # Tag it and bag it.
36
- echo TAG $tag
37
36
 
38
- gem push scientist-*.gem && git tag "$tag" # &&
39
- # git push origin master && git push origin "$tag"
37
+ gem push scientist-*.gem && git tag "$tag" &&
38
+ git push origin master && git push origin "$tag"
@@ -4,7 +4,7 @@
4
4
  set -e
5
5
 
6
6
  cd $(dirname "$0")/..
7
- script/bootstrap && ruby -I lib \
7
+ script/bootstrap && bundle exec ruby -I lib \
8
8
  -e 'require "bundler/setup"' \
9
9
  -e 'require "coveralls"; Coveralls.wear!{ add_filter ".bundle" }' \
10
10
  -e 'require "minitest/autorun"' \
@@ -239,6 +239,13 @@ describe Scientist::Experiment do
239
239
  assert_equal 10, @ex.clean_value(10)
240
240
  end
241
241
 
242
+ it "provides the clean block when asked for it, in case subclasses wish to override and provide defaults" do
243
+ assert_nil @ex.cleaner
244
+ cleaner = ->(value) { value.upcase }
245
+ @ex.clean(&cleaner)
246
+ assert_equal cleaner, @ex.cleaner
247
+ end
248
+
242
249
  it "calls the configured clean block with a value when configured" do
243
250
  @ex.clean do |value|
244
251
  value.upcase
@@ -559,4 +566,32 @@ candidate:
559
566
  refute before, "before_run should not have run"
560
567
  end
561
568
  end
569
+
570
+ describe "testing hooks for extending code" do
571
+ it "allows a user to provide fabricated durations for testing purposes" do
572
+ @ex.use { true }
573
+ @ex.try { true }
574
+ @ex.fabricate_durations_for_testing_purposes( "control" => 0.5, "candidate" => 1.0 )
575
+
576
+ @ex.run
577
+
578
+ cont = @ex.published_result.control
579
+ cand = @ex.published_result.candidates.first
580
+ assert_in_delta 0.5, cont.duration, 0.01
581
+ assert_in_delta 1.0, cand.duration, 0.01
582
+ end
583
+
584
+ it "returns actual durations if fabricated ones are omitted for some blocks" do
585
+ @ex.use { true }
586
+ @ex.try { sleep 0.1; true }
587
+ @ex.fabricate_durations_for_testing_purposes( "control" => 0.5 )
588
+
589
+ @ex.run
590
+
591
+ cont = @ex.published_result.control
592
+ cand = @ex.published_result.candidates.first
593
+ assert_in_delta 0.5, cont.duration, 0.01
594
+ assert_in_delta 0.1, cand.duration, 0.01
595
+ end
596
+ end
562
597
  end
@@ -56,27 +56,29 @@ describe Scientist do
56
56
  obj = Object.new
57
57
  obj.extend(Scientist)
58
58
 
59
- result = obj.science "test", run: "first-way" do |e|
60
- experiment = e
59
+ behaviors_executed = []
61
60
 
62
- e.try("first-way") { true }
63
- e.try("second-way") { true }
61
+ result = obj.science "test", run: "first-way" do |e|
62
+ e.try("first-way") { behaviors_executed << "first-way" ; true }
63
+ e.try("second-way") { behaviors_executed << "second-way" ; true }
64
64
  end
65
65
 
66
66
  assert_equal true, result
67
+ assert_equal [ "first-way" ], behaviors_executed
67
68
  end
68
69
 
69
70
  it "runs control when there is a nil named test" do
70
71
  obj = Object.new
71
72
  obj.extend(Scientist)
72
73
 
73
- result = obj.science "test", nil do |e|
74
- experiment = e
74
+ behaviors_executed = []
75
75
 
76
- e.use { true }
77
- e.try("second-way") { true }
76
+ result = obj.science "test", nil do |e|
77
+ e.use { behaviors_executed << "control" ; true }
78
+ e.try("second-way") { behaviors_executed << "second-way" ; true }
78
79
  end
79
80
 
80
81
  assert_equal true, result
82
+ assert_equal [ "control" ], behaviors_executed
81
83
  end
82
84
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scientist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2018-07-05 00:00:00.000000000 Z
15
+ date: 2019-04-02 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: minitest
@@ -88,15 +88,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
88
  requirements:
89
89
  - - ">="
90
90
  - !ruby/object:Gem::Version
91
- version: '2.1'
91
+ version: '2.3'
92
92
  required_rubygems_version: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  requirements: []
98
- rubyforge_project:
99
- rubygems_version: 2.7.3
98
+ rubygems_version: 3.0.3
100
99
  signing_key:
101
100
  specification_version: 4
102
101
  summary: Carefully test, measure, and track refactored code.