From 4f402440e46e5b8eecbb419508f4c2cb80c20897 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 13 Apr 2014 21:11:26 +0000 Subject: [PATCH 1/8] Add mutant dependency --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index c9783d3..66591da 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,5 @@ source 'https://rubygems.org' # Specify your gem's dependencies in harness.gemspec gemspec + +gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'minitest' From 13e8d011bb8d9a91374866ee3d89410b7e1bed2d Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 13 Apr 2014 22:13:35 +0000 Subject: [PATCH 2/8] Mutant test integration to minitest --- test/test_helper.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 97a9a49..250c233 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,12 +1,12 @@ require 'bundler/setup' -require 'simplecov' -SimpleCov.start - require 'harness' require 'minitest/unit' -require 'minitest/autorun' + +unless ENV.key?('MUTANT') + require 'minitest/autorun' +end Thread.abort_on_exception = true From e33dde836b120bb27775d199e917b9ef642bb887 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 13 Apr 2014 23:24:10 +0000 Subject: [PATCH 3/8] Use explicit signalling to detect active mutant --- test/test_helper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_helper.rb b/test/test_helper.rb index 250c233..a24ced5 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -4,7 +4,9 @@ require 'minitest/unit' -unless ENV.key?('MUTANT') +require 'mutant-minitest' + +unless Mutant::Minitest.active? require 'minitest/autorun' end From f16544febe201f712aa1dc67ba0d19defb25291f Mon Sep 17 00:00:00 2001 From: ahawkins Date: Tue, 15 Apr 2014 21:46:03 +0200 Subject: [PATCH 4/8] 97.5 mutation test coverage on Harness only --- Gemfile | 2 +- Rakefile | 4 ++ lib/harness.rb | 74 +++++------------------ lib/harness/fake_collector.rb | 12 ++-- test/acceptance_test.rb | 11 ++++ test/instrumentation_test.rb | 110 +++++++++++++++++++++++++++++----- test/test_helper.rb | 9 ++- 7 files changed, 138 insertions(+), 84 deletions(-) diff --git a/Gemfile b/Gemfile index 66591da..3755afd 100644 --- a/Gemfile +++ b/Gemfile @@ -3,4 +3,4 @@ source 'https://rubygems.org' # Specify your gem's dependencies in harness.gemspec gemspec -gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'minitest' +gem 'mutant', git: 'https://github.com/ahawkins/mutant', branch: 'minitest' diff --git a/Rakefile b/Rakefile index 877cc48..74f44fa 100644 --- a/Rakefile +++ b/Rakefile @@ -7,4 +7,8 @@ Rake::TestTask.new(:test) do |test| test.pattern = 'test/**/*_test.rb' end +task :mutant do + sh "bundle exec mutant -I lib -r harness --use minitest '::Harness'" +end + task :default => :test diff --git a/lib/harness.rb b/lib/harness.rb index 3272154..ec56a18 100644 --- a/lib/harness.rb +++ b/lib/harness.rb @@ -7,83 +7,39 @@ class Config attr_accessor :collector, :queue end - Measurement = Struct.new(:name, :value, :rate) do - def self.from_event(event) - payload = event.payload - value = payload.fetch name.split('::').last.downcase.to_sym - - case value - when true - new event.name - when Fixnum - new event.name, value - when String - new value - when Hash - new(value[:name] || event.name, value[:value], value[:rate]) - end - end - - def sample_rate - rate.nil? ? 1 : rate - end - end - - class Timer < Measurement - def self.from_event(event) - timer = super - timer.value = event.duration - timer - end - - def ms - value - end - - def log - Harness.timing name, ms, sample_rate - end - end - - class Counter < Measurement - def log - if value.nil? - Harness.increment name, sample_rate - else - Harness.count name, value, sample_rate - end - end - end - def self.config @config ||= Config.new end - def self.increment(*args) - queue.push [:increment, args] + def self.increment(stat, sample_rate = 1) + queue.push [:increment, [stat, sample_rate]] end - def self.decrement(*args) - queue.push [:decrement, args] + def self.decrement(stat, sample_rate = 1) + queue.push [:decrement, [stat, sample_rate]] end - def self.timing(*args) - queue.push [:timing, args] + def self.count(stat, value, sample_rate = 1) + queue.push [:count, [stat, value, sample_rate]] end - def self.count(*args) - queue.push [:count, args] + def self.timing(start, ms, sample_rate = 1) + queue.push [:timing, [start, ms, sample_rate]] end def self.time(stat, sample_rate = 1) start = Time.now result = yield - timing(stat, ((Time.now - start) * 1000).round, sample_rate) + timing(stat, delta(start), sample_rate) result end - def self.gauge(*args) - queue.push [:gauge, args] + def self.delta(start, finish = Time.now) + ((finish - start) * 1000).round + end + + def self.gauge(stat, value, sample_rate = 1) + queue.push [:gauge, [stat, value, sample_rate]] end def self.queue diff --git a/lib/harness/fake_collector.rb b/lib/harness/fake_collector.rb index 22e24a5..6b363d9 100644 --- a/lib/harness/fake_collector.rb +++ b/lib/harness/fake_collector.rb @@ -1,8 +1,8 @@ module Harness class FakeCollector - Increment = Struct.new(:name, :amount, :rate) - Decrement = Struct.new(:name, :amount, :rate) - Gauge = Struct.new(:name, :value, :rate) + Increment = Struct.new(:name, :rate) + Decrement = Struct.new(:name, :rate) + Measurement = Struct.new(:name, :value, :rate) attr_reader :gauges, :counters, :timers, :increments, :decrements @@ -11,7 +11,7 @@ def initialize end def timing(*args) - timers << Harness::Timer.new(*args) + timers << Measurement.new(*args) end def time(stat, sample_rate = 1) @@ -30,11 +30,11 @@ def decrement(*args) end def count(*args) - counters << Harness::Counter.new(*args) + counters << Measurement.new(*args) end def gauge(*args) - gauges << Gauge.new(*args) + gauges << Measurement.new(*args) end end end diff --git a/test/acceptance_test.rb b/test/acceptance_test.rb index f418f00..1538b77 100644 --- a/test/acceptance_test.rb +++ b/test/acceptance_test.rb @@ -17,4 +17,15 @@ def test_works_with_actual_statsd true end end + + def test_config_returns_configuration_instance + assert_instance_of Harness::Config, Harness.config + end + + def test_time_deltas_are_calculated_in_milliseconds + start = Time.now + finish = start + 2.5 + + assert_equal 2500, Harness.delta(start, finish) + end end diff --git a/test/instrumentation_test.rb b/test/instrumentation_test.rb index 3a01245..412571e 100644 --- a/test/instrumentation_test.rb +++ b/test/instrumentation_test.rb @@ -8,41 +8,121 @@ class Worker attr_reader :worker def setup + super @worker = Worker.new end - def test_can_use_increments - worker.increment 'foo', 0.5 - assert_increment 'foo' + def test_increment + worker.increment 'foo' + + assert_increment 'foo' do |counter| + assert_equal 1, counter.rate + end + + worker.increment 'bar', 0.5 + + assert_increment 'bar' do |counter| + assert_equal 0.5, counter.rate, 'Default sample rate incorrect' + end end - def test_can_use_decrements - worker.decrement 'foo', 0.5 - assert_decrement 'foo' + def test_decrement + worker.decrement 'foo' + + assert_decrement 'foo' do |counter| + assert_equal 1, counter.rate + end + + worker.decrement 'bar', 0.5 + + assert_decrement 'bar' do |counter| + assert_equal 0.5, counter.rate, 'Default sample rate incorrect' + end end - def test_can_use_count - worker.count 'foo', 1, 0.5 - assert_counter 'foo' + def test_count + worker.count 'foo', 6 + + assert_counter 'foo' do |counter| + assert_equal 6, counter.value + assert_equal 1, counter.rate, 'Default sample rate incorrect' + end + + worker.count 'bar', 1, 0.5 + + assert_counter 'bar' do |counter| + assert_equal 1, counter.value + assert_equal 0.5, counter.rate + end end def test_can_use_gauges - worker.gauge 'foo', 5, 0.5 - assert_gauge 'foo' + worker.gauge 'foo', 6 + + assert_gauge 'foo' do |gauge| + assert_equal 6, gauge.value + assert_equal 1, gauge.rate, 'Default sample rate incorrect' + end + + worker.gauge 'bar', 1, 0.5 + + assert_gauge 'bar' do |gauge| + assert_equal 1, gauge.value + assert_equal 0.5, gauge.rate + end end def test_can_use_timings - worker.timing 'foo', 5, 0.5 - assert_timer 'foo' + worker.timing 'foo', 6 + + assert_timer 'foo' do |timer| + assert_equal 6, timer.value + assert_equal 1, timer.rate, 'Default sample rate incorrect' + end + + worker.timing 'bar', 1, 0.5 + + assert_timer 'bar' do |timer| + assert_equal 1, timer.value + assert_equal 0.5, timer.rate + end end def test_can_be_timed - result = worker.time 'foo', 0.5 do + result = worker.time 'foo' do + 'bar' + end + + assert_timer 'foo' do |timer| + assert_instance_of Fixnum, timer.value + assert_equal 1, timer.rate, 'Default sample rate incorrect' + end + + result = worker.time 'baz', 0.5 do + 'bar' + end + + assert_timer 'baz' do |timer| + assert_equal 0.5, timer.rate + end + end + + def test_time_returns_block_value + result = worker.time 'foo' do 'bar' end assert_equal 'bar', result, "#time did not return block's value" - assert_timer 'foo' + end + + def test_timer_use_1_for_default_sample_rate + result = worker.time 'foo' do + 'bar' + end + + assert_timer 'foo' do |timer| + assert_equal 1, timer.rate + end end def test_also_works_with_extend diff --git a/test/test_helper.rb b/test/test_helper.rb index a24ced5..d7f2453 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -6,9 +6,7 @@ require 'mutant-minitest' -unless Mutant::Minitest.active? - require 'minitest/autorun' -end +require 'minitest/autorun' unless Mutant::Minitest.active? Thread.abort_on_exception = true @@ -22,30 +20,35 @@ def assert_timer(name) refute_empty timers timer = timers.find { |t| t.name == name } assert timer, "Timer #{name} not logged!" + yield timer if block_given? end def assert_increment(name) refute_empty increments increment = increments.find { |i| i.name == name } assert increment, "Increment #{name} not logged!" + yield increment if block_given? end def assert_decrement(name) refute_empty decrements decrement = decrements.find { |i| i.name == name } assert decrement, "decrement #{name} not logged!" + yield decrement if block_given? end def assert_gauge(name) refute_empty gauges gauge = gauges.find { |g| g.name == name } assert gauge, "gauge #{name} not logged!" + yield gauge if block_given? end def assert_counter(name) refute_empty counters counter = counters.find { |g| g.name == name } assert counter, "counter #{name} not logged!" + yield counter if block_given? end def collector From b3f00bd2833a82b6ade419feea11f4f3623fe7ab Mon Sep 17 00:00:00 2001 From: ahawkins Date: Tue, 15 Apr 2014 22:46:12 +0200 Subject: [PATCH 5/8] NullCollector 100% mutation coverage --- lib/harness/null_collector.rb | 4 ++-- test/null_collector_test.rb | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/harness/null_collector.rb b/lib/harness/null_collector.rb index 440a0ae..c8ec675 100644 --- a/lib/harness/null_collector.rb +++ b/lib/harness/null_collector.rb @@ -12,8 +12,8 @@ def count(*args) end - def time(*args, &block) - yield block + def time(*args) + yield end def timing(*args) diff --git a/test/null_collector_test.rb b/test/null_collector_test.rb index 9adb928..a669055 100644 --- a/test/null_collector_test.rb +++ b/test/null_collector_test.rb @@ -8,11 +8,15 @@ def setup end def test_respond_to_increment - statsd.increment 'foo', 5, 0.5 + statsd.increment 'foo', 0.5 end def test_respond_to_decrement - statsd.decrement 'foo', 5, 0.5 + statsd.decrement 'foo', 0.5 + end + + def test_respond_to_count + statsd.count 'foo', 5, 0.5 end def test_responds_to_timing From bcb9a0bc71cdec4ef371e1e75773acdd2949d154 Mon Sep 17 00:00:00 2001 From: ahawkins Date: Tue, 15 Apr 2014 22:49:18 +0200 Subject: [PATCH 6/8] FakeCollector 100% mutation covered Remove unused method --- lib/harness/fake_collector.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/harness/fake_collector.rb b/lib/harness/fake_collector.rb index 6b363d9..ed8be7b 100644 --- a/lib/harness/fake_collector.rb +++ b/lib/harness/fake_collector.rb @@ -14,13 +14,6 @@ def timing(*args) timers << Measurement.new(*args) end - def time(stat, sample_rate = 1) - start = Time.now - result = yield - timing(stat, ((Time.now - start) * 1000).round, sample_rate) - result - end - def increment(*args) increments << Increment.new(*args) end From c5cc8a3914990a95d784d885da41b94f23bc1157 Mon Sep 17 00:00:00 2001 From: ahawkins Date: Wed, 16 Apr 2014 23:48:18 +0200 Subject: [PATCH 7/8] 100% mutation cover AsyncQueue --- lib/harness/async_queue.rb | 15 ++++++---- test/async_queue_test.rb | 59 ++++++++++++++++++++++++++++++++++++-- test/test_helper.rb | 2 -- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/lib/harness/async_queue.rb b/lib/harness/async_queue.rb index 6974a55..352d03c 100644 --- a/lib/harness/async_queue.rb +++ b/lib/harness/async_queue.rb @@ -2,10 +2,11 @@ module Harness class AsyncQueue - attr_reader :consumer, :queue + attr_reader :queue, :consumer + + def initialize(queue = Queue.new, collector = Harness.collector) + @queue = queue - def initialize - @queue = Queue.new @consumer = Thread.new do loop do msg = queue.pop @@ -22,8 +23,12 @@ def push(msg) queue.push msg end - def collector - Harness.collector + def stop + consumer.kill + end + + def up? + consumer.alive? end end end diff --git a/test/async_queue_test.rb b/test/async_queue_test.rb index b606332..7c462f9 100644 --- a/test/async_queue_test.rb +++ b/test/async_queue_test.rb @@ -1,14 +1,69 @@ require_relative 'test_helper' -Thread.abort_on_exception = true class AsyncQueueTest < MiniTest::Unit::TestCase + attr_reader :queue, :collector + + class NullQueue + def push(*) + + end + end + + def setup + super + Harness.config.queue = NullQueue.new + + @collector = Harness::FakeCollector.new + @queue = Harness::AsyncQueue.new Queue.new, collector + assert queue.up?, 'Precondition: consumer not running' + end + + def teardown + queue.stop + super + end + + def test_proceses_queue_after_instantiating + q = Queue.new + q << [:gauge, ['foo', 5]] + + @queue = Harness::AsyncQueue.new q, collector + + sleep 0.1 + + assert queue.up?, 'Queue should be working' + + assert_gauge 'foo' + end + def test_uses_consumer_thread_to_not_block_the_main_thread - queue = Harness::AsyncQueue.new + queue.push [:gauge, ['foo', 5]] + + sleep 0.1 + + assert queue.up?, 'Queue should be working' + + assert_gauge 'foo' + end + + def test_use_configured_collector_by_default + Harness.config.collector = collector + + @queue = Harness::AsyncQueue.new queue.push [:gauge, ['foo', 5]] sleep 0.1 + assert queue.up?, 'Queue should be working' + assert_gauge 'foo' end + + def test_consumer_can_be_stopped + assert queue.up?, 'Consumer must be alive' + queue.stop + sleep 0.1 + refute queue.up?, 'Consumer should be killed' + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index d7f2453..324c848 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -8,8 +8,6 @@ require 'minitest/autorun' unless Mutant::Minitest.active? -Thread.abort_on_exception = true - class MiniTest::Unit::TestCase def setup Harness.config.collector = Harness::FakeCollector.new From 8c42625e58c2a649ec0bfe6d054f1a86c6ce733e Mon Sep 17 00:00:00 2001 From: ahawkins Date: Fri, 18 Apr 2014 12:28:16 +0200 Subject: [PATCH 8/8] Fix config related mutations --- lib/harness.rb | 8 +++++--- test/acceptance_test.rb | 4 ---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/harness.rb b/lib/harness.rb index ec56a18..3ef0dd4 100644 --- a/lib/harness.rb +++ b/lib/harness.rb @@ -1,14 +1,16 @@ require "harness/version" require 'statsd' +require 'singleton' module Harness - class Config + Config = Class.new do + include Singleton attr_accessor :collector, :queue - end + end.instance def self.config - @config ||= Config.new + Config end def self.increment(stat, sample_rate = 1) diff --git a/test/acceptance_test.rb b/test/acceptance_test.rb index 1538b77..8693419 100644 --- a/test/acceptance_test.rb +++ b/test/acceptance_test.rb @@ -18,10 +18,6 @@ def test_works_with_actual_statsd end end - def test_config_returns_configuration_instance - assert_instance_of Harness::Config, Harness.config - end - def test_time_deltas_are_calculated_in_milliseconds start = Time.now finish = start + 2.5