Skip to content

Commit

Permalink
Merge ef49c7c into f53aadf
Browse files Browse the repository at this point in the history
  • Loading branch information
greenkeeperio-bot committed Jul 5, 2016
2 parents f53aadf + ef49c7c commit 15aa469
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 41 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"chai": "^3.4.1",
"chai-as-promised": "^5.1.0",
"coveralls": "^2.11.8",
"flow-bin": "^0.27.0",
"flow-bin": "^0.28.0",
"mocha": "^2.5.1",
"nyc": "^6.6.1",
"sinon": "^1.17.2",
Expand Down
30 changes: 16 additions & 14 deletions src/experiment.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import find from '101/find'
import hasProperties from '101/has-properties'
import isFunction from '101/is-function'
import isObject from '101/is-object'
import isString from '101/is-string'
import KnuthShuffle from 'knuth-shuffle'
import Promise from 'bluebird'

Expand All @@ -19,11 +18,11 @@ const debug = Debug('scientist:experiment')

class Experiment<V> {
_before_run_fn: Function;
_behaviors: Map;
_behaviors: Map<string, Function>;
_cleaner_fn: Function;
_comparator: Function;
_context: Object;
_ignores: List;
_ignores: List<(control: V, observation: V) => boolean>;
_raise_on_mismatches: boolean;
_run_if_fn: Function;
enabled: boolean;
Expand Down Expand Up @@ -59,7 +58,7 @@ class Experiment<V> {
* @param {Function} fn Function to clean the value. Must take one argument,
* the value to be cleaned.
*/
clean (fn: (value: V) => V) {
clean (fn: (value: V) => V): void {
debug('clean')
this._cleaner_fn = fn
}
Expand All @@ -84,7 +83,7 @@ class Experiment<V> {
* @param {Function} fn Function to compare. Must accept two arguments, the
* control and candidate values, and return true or false.
*/
compare (fn: (a: V, b: V) => boolean) {
compare (fn: (a: V, b: V) => boolean): void {
debug('compare')
this._comparator = fn
}
Expand All @@ -108,7 +107,7 @@ class Experiment<V> {
* do not match. If the function returns true, the mismatch is discarded.
* @param {Function} fn Function that returns a boolean about a match.
*/
ignore (fn: (o: Observation) => boolean) {
ignore (fn: (control: V, o: V) => boolean): void {
debug('ignore')
this._ignores = this._ignores.push(fn)
}
Expand All @@ -122,8 +121,8 @@ class Experiment<V> {
* @return {boolean} Returns true if the pair should be ignored.
*/
ignoreMismatchedObservation (
control: Observation,
candidate: Observation
control: Observation<V>,
candidate: Observation<V>
): boolean {
debug('ignoreMismatchedObservation')
if (this._ignores.size === 0) {
Expand All @@ -140,8 +139,8 @@ class Experiment<V> {
* @return {Boolean} True if the two observations are equivalent.
*/
observationsAreEquivalent (
control: Observation,
candidate: Observation
control: Observation<V>,
candidate: Observation<V>
): boolean {
debug('observationsAreEquivalent')
if (isFunction(this._comparator)) {
Expand Down Expand Up @@ -215,7 +214,7 @@ class Experiment<V> {
* @param {Function} fn Function determining fate of experiment. Returns true or
* false.
*/
runIf (fn: () => boolean) {
runIf (fn: () => boolean): void {
debug('runIf')
this._run_if_fn = fn
}
Expand Down Expand Up @@ -253,19 +252,22 @@ class Experiment<V> {
}

// FIXME(@bkendall): I dislike this...
try (name: string | Function, fn: ?string | Function) {
try (name: string | Function, fn?: Function): void {
debug('try')
if (!isString(name)) {
if (typeof name === 'function') {
fn = name
name = 'candidate'
}
if (this._behaviors.has(name)) {
throw new Error(`Name (${name}) is not unique for behavior`)
}
if (typeof fn !== 'function') {
throw new Error('.try: Function is not a function.')
}
this._behaviors = this._behaviors.set(name, fn)
}

use (fn: Function) {
use (fn: Function): void {
debug('use')
this.try('control', fn)
}
Expand Down
14 changes: 9 additions & 5 deletions src/observation.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,24 @@ import Experiment from './experiment'
class Observation<V> {
duration: number;
exception: Error;
experiment: Experiment;
experiment: Experiment<V>;
fn: Function;
name: string;
now: number;
value: V;

constructor (name: string, experiment: Experiment, fn: Function) {
constructor (name: string, experiment: Experiment<V>, fn: Function) {
this.name = name
this.experiment = experiment
this.now = Date.now()
this.fn = fn
}

static create (name, experiment, fn) {
static create (
name: string,
experiment: Experiment<V>,
fn: Function
): Promise<Observation<V>> {
const observation = new Observation(name, experiment, fn)
return Promise.resolve()
.then(() => (observation.fn()))
Expand All @@ -38,7 +42,7 @@ class Observation<V> {
* block to clean the observed value.
* @return {Object} Cleaned value.
*/
cleanedValue () {
cleanedValue (): ?V {
if (this.value) {
return this.experiment.cleanValue(this.value)
}
Expand All @@ -63,7 +67,7 @@ class Observation<V> {
* @return {Boolean} True if they are equivalent.
*/
equivalentTo (
other: Observation,
other: Observation<V>,
comparator: (a: V, b: V) => boolean
): boolean {
if (!(other instanceof Observation)) {
Expand Down
28 changes: 14 additions & 14 deletions src/result.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import Observation from './observation'
const debug = Debug('scientist:result')

class Result {
_ignored: List<Observation>;
_mismatched: List<Observation>;
candidates: List<Observation>;
control: Observation;
experiment: Experiment;
observations: Array<Observation>;
_ignored: List<Observation<*>>;
_mismatched: List<Observation<*>>;
candidates: List<Observation<*>>;
control: Observation<*>;
experiment: Experiment<*>;
observations: Array<Observation<*>>;

constructor (
experiment: Experiment,
observations: Array<Observation>,
control: Observation
experiment: Experiment<*>,
observations: Array<Observation<*>>,
control: Observation<*>
) {
debug('constructor')
this.experiment = experiment
Expand All @@ -45,10 +45,10 @@ class Result {
* @return {Result} New Result.
*/
static create (
experiment: Experiment,
observations: Array<Observation>,
control: Observation
) {
experiment: Experiment<*>,
observations: Array<Observation<*>>,
control: Observation<*>
): Result {
debug('create')
return new Result(experiment, observations, control)
}
Expand Down Expand Up @@ -103,7 +103,7 @@ class Result {
* ._mismatched and ._ignored with appropriate candidates.
* @private
*/
evaluateCandidates () {
evaluateCandidates (): void {
let mismatched = this.candidates.filter((candidate) => {
return !this.experiment.observationsAreEquivalent(this.control, candidate)
})
Expand Down
2 changes: 1 addition & 1 deletion src/scientist.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Experiment from './experiment'
class Scientist {
static Experiment: typeof Experiment;

science (name: string, opts: Object = {}): Experiment {
science (name: string, opts: Object = {}): Experiment<*> {
const Type = opts.Experiment || Experiment
const experiment = new Type(name)
experiment.context({})
Expand Down
19 changes: 13 additions & 6 deletions test/unit/src/experiment.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ describe('Experiment', function () {
describe('shouldExperimentRun', function () {
describe('with multiple behaviors', function () {
beforeEach(function () {
experiment.use(Promise.resolve(1))
experiment.try(Promise.resolve(2))
experiment.use(function () { Promise.resolve(1) })
experiment.try(function () { Promise.resolve(2) })
})

describe('with no fn', function () {
Expand Down Expand Up @@ -254,24 +254,31 @@ describe('Experiment', function () {

describe('try', function () {
it('should default the name to candidate', function () {
var p = Promise.resolve()
var p = function () { Promise.resolve() }
experiment.try(p)
assert.equal(experiment._behaviors.size, 1)
assert.deepEqual(experiment._behaviors.get('candidate'), p)
})

it('should throw if name added twice', function () {
var p = Promise.resolve()
var p = function () { Promise.resolve() }
experiment.try(p)
assert.throws(function () { experiment.try(p) }, /not unique/)
})

it('should add with the correct name to behaviors', function () {
var p = Promise.resolve()
var p = function () { Promise.resolve() }
experiment.try('foo', p)
assert.equal(experiment._behaviors.size, 1)
assert.deepEqual(experiment._behaviors.get('foo'), p)
})

it('should throw an error if we do not give it a function', function () {
assert.throws(
function () { experiment.try(true) },
/Function.+function/
)
})
})

describe('use', function () {
Expand All @@ -283,7 +290,7 @@ describe('Experiment', function () {
})

it('should use try to add the control', function () {
var p = Promise.resolve()
var p = function () { Promise.resolve() }
experiment.use(p)
sinon.assert.calledOnce(Experiment.prototype.try)
sinon.assert.calledWithExactly(Experiment.prototype.try, 'control', p)
Expand Down

0 comments on commit 15aa469

Please sign in to comment.