musical_spec 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/README.md +30 -0
- data/Rakefile +7 -0
- data/lib/musical_spec/bloops.rb +36 -0
- data/lib/musical_spec/formatter.rb +54 -0
- data/lib/musical_spec/note.rb +67 -0
- data/lib/musical_spec/player.rb +18 -0
- data/lib/musical_spec/version.rb +3 -0
- data/lib/musical_spec.rb +10 -0
- data/musical_spec.gemspec +23 -0
- data/spec/musical_spec/formatter_spec.rb +26 -0
- data/spec/musical_spec/note_spec.rb +72 -0
- data/spec/musical_spec/player_spec.rb +36 -0
- data/spec/musical_spec_spec.rb +7 -0
- data/spec/spec_helper.rb +10 -0
- metadata +112 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# musical_spec
|
2
|
+
|
3
|
+
An RSpec formatter that plays higher notes as tests pass and lower notes as
|
4
|
+
tests fail. A revelatory auditory experience! It's a subclass of the progress
|
5
|
+
formatter, so you still get your pretty dots.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
This uses [Bloopsaphone](https://github.com/mental/bloopsaphone), which requires PortAudio:
|
10
|
+
|
11
|
+
brew install portaudio
|
12
|
+
|
13
|
+
In your Gemfile:
|
14
|
+
|
15
|
+
gem 'musical_spec'
|
16
|
+
|
17
|
+
Then run your specs like this:
|
18
|
+
|
19
|
+
$ rspec --format MusicalSpec::Formatter spec/
|
20
|
+
|
21
|
+
To always run your specs with MusicalSpec, add the option to your `.rspec` file:
|
22
|
+
|
23
|
+
--format MusicalSpec::Formatter
|
24
|
+
|
25
|
+
## Caveats
|
26
|
+
|
27
|
+
Due to Bloopsaphone wackiness, this library has to `sleep` while the sounds
|
28
|
+
are playing. This will probably slow your test suite down a LOT.
|
29
|
+
Also, sometimes the sounds can't keep up with the test suite and some of the
|
30
|
+
sounds don't play. The dots still show up though.
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
class SoundPlayer
|
3
|
+
def initialize
|
4
|
+
@bloopsaphone = Bloops.new
|
5
|
+
@sound = @bloopsaphone.sound(Bloops::SQUARE)
|
6
|
+
@sound.sustain = 1
|
7
|
+
|
8
|
+
@note = Note.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
200.times do
|
13
|
+
@bloopsaphone.clear
|
14
|
+
if success?
|
15
|
+
@note.next!
|
16
|
+
else
|
17
|
+
@note.next!
|
18
|
+
# @note.prev!
|
19
|
+
end
|
20
|
+
|
21
|
+
@bloopsaphone.tune(@sound, @note.note)
|
22
|
+
@bloopsaphone.play
|
23
|
+
sleep 0.1 #while !@bloopsaphone.stopped?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def success?
|
30
|
+
rand(2) == 0
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
player = SoundPlayer.new
|
36
|
+
player.start
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# RSpec will only require this file, so require the rest of the gem.
|
2
|
+
require 'musical_spec'
|
3
|
+
require 'rspec/core/formatters/progress_formatter'
|
4
|
+
|
5
|
+
module MusicalSpec
|
6
|
+
class Formatter < RSpec::Core::Formatters::ProgressFormatter
|
7
|
+
def initialize(output)
|
8
|
+
super(output)
|
9
|
+
@note = MusicalSpec::Note.new
|
10
|
+
@player = MusicalSpec::Player.new
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :player
|
14
|
+
|
15
|
+
# Plays a higher note and then calls super.
|
16
|
+
def example_passed(example)
|
17
|
+
play_higher_note
|
18
|
+
super(example)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Plays a lower note and then calls super.
|
22
|
+
def example_failed(example)
|
23
|
+
play_lower_note
|
24
|
+
super(example)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Plays a note without changing the pitch and then calls super.
|
28
|
+
def example_pending(example)
|
29
|
+
play_note
|
30
|
+
super(example)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def play_note
|
36
|
+
@player.play(note)
|
37
|
+
end
|
38
|
+
|
39
|
+
def play_higher_note
|
40
|
+
@note.next!
|
41
|
+
play_note
|
42
|
+
end
|
43
|
+
|
44
|
+
def play_lower_note
|
45
|
+
@note.prev!
|
46
|
+
play_note
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def note
|
51
|
+
@note.note
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module MusicalSpec
|
2
|
+
# Uses scientific notation, e.g. C4 is middle C.
|
3
|
+
class Note
|
4
|
+
# The highest octave this will play. If it goes higher than this, it will be
|
5
|
+
# normalized back down to MAX_OCTAVE.
|
6
|
+
MAX_OCTAVE = 6
|
7
|
+
# The lowest octave this will play. If it goes lower than this, it will be
|
8
|
+
# normalized back up to MIN_OCTAVE.
|
9
|
+
MIN_OCTAVE = 2
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@letter = 'C'
|
13
|
+
@octave = 4
|
14
|
+
end
|
15
|
+
|
16
|
+
# A string like "C4". Octave (the number) does not go above MAX_OCTAVE or
|
17
|
+
# below MIN_OCTAVE.
|
18
|
+
def note
|
19
|
+
"#{@letter}#{normalized_octave}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set the note to a new one, e.g. `note.note = 'A5'`
|
23
|
+
def note=(new_note)
|
24
|
+
new_letter, new_octave = new_note.split('')
|
25
|
+
@letter = new_letter
|
26
|
+
@octave = new_octave.to_i
|
27
|
+
|
28
|
+
note
|
29
|
+
end
|
30
|
+
|
31
|
+
# Increase the pitch, handling octave changes.
|
32
|
+
def next!
|
33
|
+
if @letter == 'B'
|
34
|
+
@letter = 'C'
|
35
|
+
@octave += 1
|
36
|
+
elsif @letter == 'G'
|
37
|
+
@letter = 'A'
|
38
|
+
else
|
39
|
+
@letter.next!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Decrease the pitch, handling octave changes.
|
44
|
+
def prev!
|
45
|
+
if @letter == 'C'
|
46
|
+
@letter = 'B'
|
47
|
+
@octave -= 1
|
48
|
+
elsif @letter == 'A'
|
49
|
+
@letter = 'G'
|
50
|
+
else
|
51
|
+
@letter = (@letter.ord - 1).chr
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def normalized_octave
|
58
|
+
if @octave > MAX_OCTAVE
|
59
|
+
MAX_OCTAVE
|
60
|
+
elsif @octave < MIN_OCTAVE
|
61
|
+
MIN_OCTAVE
|
62
|
+
else
|
63
|
+
@octave
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MusicalSpec
|
2
|
+
# This class plays the music using Bloopsaphone.
|
3
|
+
class Player
|
4
|
+
def initialize
|
5
|
+
@sound = ONE_TRUE_BLOOPSAPHONE.sound(Bloops::SQUARE)
|
6
|
+
@sound.sustain = 0.01
|
7
|
+
ONE_TRUE_BLOOPSAPHONE.tempo = 320
|
8
|
+
end
|
9
|
+
|
10
|
+
# Takes a note string like "C4".
|
11
|
+
def play(note)
|
12
|
+
ONE_TRUE_BLOOPSAPHONE.clear
|
13
|
+
ONE_TRUE_BLOOPSAPHONE.tune(@sound, note)
|
14
|
+
ONE_TRUE_BLOOPSAPHONE.play
|
15
|
+
sleep 0.01 while ! ONE_TRUE_BLOOPSAPHONE.stopped?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/musical_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/musical_spec/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Gabe Berke-Williams"]
|
6
|
+
gem.email = ["gabe@thoughtbot.com"]
|
7
|
+
gem.description = %q{A musical formatter for RSpec.}
|
8
|
+
gem.summary = %q{A musical formatter for RSpec. The sound gets lower as failures pile up, and higher when tests pass.}
|
9
|
+
gem.homepage = "" # FIXME
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "musical_spec"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = MusicalSpec::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency('bloopsaphone', '~> 0.4')
|
19
|
+
gem.add_dependency('rspec-core', '~> 2.0')
|
20
|
+
|
21
|
+
gem.add_development_dependency('rspec', '~> 2.0')
|
22
|
+
gem.add_development_dependency('bourne', '~> 1.0')
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
describe MusicalSpec::Formatter do
|
5
|
+
before { subject.player.stubs(:play) }
|
6
|
+
|
7
|
+
it { should be_a RSpec::Core::Formatters::ProgressFormatter }
|
8
|
+
|
9
|
+
it 'plays a higher-pitched sound when an example passes' do
|
10
|
+
subject.example_passed(example)
|
11
|
+
subject.player.should have_received(:play).with('D4')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'plays a lower-pitched sound when an example fails' do
|
15
|
+
subject.example_failed(example)
|
16
|
+
subject.player.should have_received(:play).with('B3')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'does not change the note when an example is pending' do
|
20
|
+
subject.example_pending(example)
|
21
|
+
subject.player.should have_received(:play).with('C4')
|
22
|
+
end
|
23
|
+
|
24
|
+
subject { MusicalSpec::Formatter.new(StringIO.new) }
|
25
|
+
let(:example){ RSpec::Core::ExampleGroup.describe.example }
|
26
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MusicalSpec::Note do
|
4
|
+
it 'defaults to middle C' do
|
5
|
+
subject.note.should == 'C4'
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'sets MAX_OCTAVE to 6' do
|
9
|
+
MusicalSpec::Note::MAX_OCTAVE.should == 6
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'sets MIN_OCTAVE to 2' do
|
13
|
+
MusicalSpec::Note::MIN_OCTAVE.should == 2
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'allows setting the note' do
|
17
|
+
subject.note = 'A4'
|
18
|
+
subject.note.should == 'A4'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'does not let the octave go above MAX_OCTAVE' do
|
22
|
+
very_high_octave = MusicalSpec::Note::MAX_OCTAVE + 1
|
23
|
+
subject.note = "A#{very_high_octave}"
|
24
|
+
subject.note.should == "A#{MusicalSpec::Note::MAX_OCTAVE}"
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does not let the octave go below MIN_OCTAVE' do
|
28
|
+
very_low_octave = MusicalSpec::Note::MIN_OCTAVE - 1
|
29
|
+
subject.note = "A#{very_low_octave}"
|
30
|
+
subject.note.should == "A#{MusicalSpec::Note::MIN_OCTAVE}"
|
31
|
+
end
|
32
|
+
|
33
|
+
context '#next!' do
|
34
|
+
it 'increases the note by 1' do
|
35
|
+
subject.note = 'C4'
|
36
|
+
subject.next!
|
37
|
+
subject.note.should == 'D4'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'changes octaves when moving from B to C' do
|
41
|
+
subject.note = 'B4'
|
42
|
+
subject.next!
|
43
|
+
subject.note.should == 'C5'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'does not change octaves when moving from G to A' do
|
47
|
+
subject.note = 'G4'
|
48
|
+
subject.next!
|
49
|
+
subject.note.should == 'A4'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context '#prev!' do
|
54
|
+
it 'decreases the note by 1' do
|
55
|
+
subject.note = 'E4'
|
56
|
+
subject.prev!
|
57
|
+
subject.note.should == 'D4'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'changes octaves when moving from C to B' do
|
61
|
+
subject.note = 'C4'
|
62
|
+
subject.prev!
|
63
|
+
subject.note.should == 'B3'
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'does not change octaves when moving from A to G' do
|
67
|
+
subject.note = 'A4'
|
68
|
+
subject.prev!
|
69
|
+
subject.note.should == 'G4'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MusicalSpec::Player do
|
4
|
+
before do
|
5
|
+
@real_bloopsaphone = MusicalSpec::ONE_TRUE_BLOOPSAPHONE
|
6
|
+
MusicalSpec::ONE_TRUE_BLOOPSAPHONE = fake_bloopsaphone
|
7
|
+
end
|
8
|
+
|
9
|
+
after { MusicalSpec::ONE_TRUE_BLOOPSAPHONE = @real_bloopsaphone }
|
10
|
+
|
11
|
+
it 'sets the correct note' do
|
12
|
+
subject.play('C4')
|
13
|
+
fake_bloopsaphone.should have_received(:tune).with(fake_sound, 'C4')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'clears tunes before playing' do
|
17
|
+
subject.play('C4')
|
18
|
+
fake_bloopsaphone.should have_received(:clear).once
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'plays the tune' do
|
22
|
+
subject.play('C4')
|
23
|
+
fake_bloopsaphone.should have_received(:play).once
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:fake_sound) { stub("Bloopsaphone sound", :sustain= => nil) }
|
27
|
+
let(:fake_bloopsaphone) do
|
28
|
+
stub('Fake Bloopsaphone',
|
29
|
+
:tune => nil,
|
30
|
+
:tempo= => nil,
|
31
|
+
:play => nil,
|
32
|
+
:clear => nil,
|
33
|
+
:stopped? => true,
|
34
|
+
:sound => fake_sound)
|
35
|
+
end
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: musical_spec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gabe Berke-Williams
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-27 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bloopsaphone
|
16
|
+
requirement: &2153435480 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.4'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2153435480
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec-core
|
27
|
+
requirement: &2153433840 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2153433840
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &2153433380 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2153433380
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bourne
|
49
|
+
requirement: &2153432720 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2153432720
|
58
|
+
description: A musical formatter for RSpec.
|
59
|
+
email:
|
60
|
+
- gabe@thoughtbot.com
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- .gitignore
|
66
|
+
- Gemfile
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- lib/musical_spec.rb
|
70
|
+
- lib/musical_spec/bloops.rb
|
71
|
+
- lib/musical_spec/formatter.rb
|
72
|
+
- lib/musical_spec/note.rb
|
73
|
+
- lib/musical_spec/player.rb
|
74
|
+
- lib/musical_spec/version.rb
|
75
|
+
- musical_spec.gemspec
|
76
|
+
- spec/musical_spec/formatter_spec.rb
|
77
|
+
- spec/musical_spec/note_spec.rb
|
78
|
+
- spec/musical_spec/player_spec.rb
|
79
|
+
- spec/musical_spec_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
homepage: ''
|
82
|
+
licenses: []
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.8.11
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: A musical formatter for RSpec. The sound gets lower as failures pile up,
|
105
|
+
and higher when tests pass.
|
106
|
+
test_files:
|
107
|
+
- spec/musical_spec/formatter_spec.rb
|
108
|
+
- spec/musical_spec/note_spec.rb
|
109
|
+
- spec/musical_spec/player_spec.rb
|
110
|
+
- spec/musical_spec_spec.rb
|
111
|
+
- spec/spec_helper.rb
|
112
|
+
has_rdoc:
|