scaffolder-tools 0.1.0
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/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +23 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +38 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/bin/scaffolder +14 -0
- data/features/help.feature +79 -0
- data/features/sequence.feature +157 -0
- data/features/step_definitions/scaffolder-tools.rb +4 -0
- data/features/support/env.rb +15 -0
- data/features/validate.feature +245 -0
- data/lib/scaffolder/binary_helper.rb +30 -0
- data/lib/scaffolder/tool/help.rb +57 -0
- data/lib/scaffolder/tool/sequence.rb +34 -0
- data/lib/scaffolder/tool/validate.rb +51 -0
- data/lib/scaffolder/tool.rb +44 -0
- data/lib/scaffolder/tool_index.rb +50 -0
- data/man/scaffolder-help.1.ronn +23 -0
- data/man/scaffolder-sequence.1.ronn +48 -0
- data/man/scaffolder-validate.1.ronn +28 -0
- data/scaffolder-tools.gemspec +125 -0
- data/spec/scaffolder/binary_helper_spec.rb +71 -0
- data/spec/scaffolder/tool/help_spec.rb +127 -0
- data/spec/scaffolder/tool/sequence_spec.rb +41 -0
- data/spec/scaffolder/tool/validate_spec.rb +167 -0
- data/spec/scaffolder/tool_index_spec.rb +49 -0
- data/spec/scaffolder/tool_spec.rb +147 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/exit_code_matcher.rb +26 -0
- metadata +330 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'..','..','spec_helper'))
|
2
|
+
|
3
|
+
describe Scaffolder::Tool::Sequence do
|
4
|
+
|
5
|
+
it "should inherit from Scaffolder::Tool" do
|
6
|
+
described_class.superclass.should == Scaffolder::Tool
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return the description of the tool" do
|
10
|
+
desc = "Generate the fasta output for the scaffold"
|
11
|
+
described_class.description.should == desc
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "execution when correctly instantiated" do
|
15
|
+
|
16
|
+
before(:each) do
|
17
|
+
entries = [{:name => 'seq1', :nucleotides => 'ATGC'}]
|
18
|
+
|
19
|
+
@scaffold_file = File.new("scaffold",'w').path
|
20
|
+
@sequence_file = File.new("sequence",'w').path
|
21
|
+
|
22
|
+
write_scaffold_file(entries,@scaffold_file)
|
23
|
+
write_sequence_file(entries,@sequence_file)
|
24
|
+
settings = mock_command_line_settings(@scaffold_file,@sequence_file,{
|
25
|
+
:definition => nil,:no => nil})
|
26
|
+
|
27
|
+
tool = described_class.new(settings)
|
28
|
+
@output = StringIO.new(tool.execute)
|
29
|
+
end
|
30
|
+
|
31
|
+
after(:each) do
|
32
|
+
File.delete @scaffold_file, @sequence_file
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should return the expected sequence" do
|
36
|
+
Bio::FlatFile.auto(@output).first.seq.should == 'ATGC'
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'..','..','spec_helper'))
|
2
|
+
|
3
|
+
describe Scaffolder::Tool::Validate do
|
4
|
+
|
5
|
+
it "should inherit from Scaffolder::Tool" do
|
6
|
+
described_class.superclass.should == Scaffolder::Tool
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return the description of the tool" do
|
10
|
+
desc = "Validate scaffold for overlapping inserts"
|
11
|
+
described_class.description.should == desc
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "comparing inserts for overlaps" do
|
15
|
+
|
16
|
+
before(:all) do
|
17
|
+
@insert = stub(:position => 5..10)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be false when inserts don't overlap" do
|
21
|
+
no_overlap = stub(:position => 11..12)
|
22
|
+
described_class.inserts_overlap?(@insert,no_overlap).should be_false
|
23
|
+
described_class.inserts_overlap?(no_overlap,@insert).should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be true when inserts do overlap" do
|
27
|
+
overlap = stub(:position => 10..11)
|
28
|
+
described_class.inserts_overlap?(@insert,overlap).should be_true
|
29
|
+
described_class.inserts_overlap?(overlap,@insert).should be_true
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "testing a sequence for overlapping inserts" do
|
35
|
+
|
36
|
+
before(:all) do
|
37
|
+
# Use integers for mocks because they can be easily sorted
|
38
|
+
@a = 1; @b = 2
|
39
|
+
@sequence = stub(:inserts => [@a,@b])
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return an empty array when sequence has no errors" do
|
43
|
+
described_class.stubs(:inserts_overlap?).with(@a,@b).returns(false)
|
44
|
+
described_class.stubs(:inserts_overlap?).with(@b,@a).returns(false)
|
45
|
+
described_class.sequence_errors(@sequence).empty?.should be_true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return inserts when sequence inserts overlap" do
|
49
|
+
described_class.stubs(:inserts_overlap?).with(@a,@b).returns(true)
|
50
|
+
described_class.stubs(:inserts_overlap?).with(@b,@a).returns(true)
|
51
|
+
described_class.sequence_errors(@sequence).should == [[@a,@b]].sort
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "validating each entry in the scaffold" do
|
57
|
+
|
58
|
+
before(:each) do
|
59
|
+
@valid = stub(:entry_type => :sequence)
|
60
|
+
@invalid = @valid.clone
|
61
|
+
described_class.stubs(:sequence_errors).with(@valid).returns([])
|
62
|
+
described_class.stubs(:sequence_errors).with(@invalid).returns([nil])
|
63
|
+
end
|
64
|
+
|
65
|
+
subject do
|
66
|
+
described_class.new(mock_command_line_settings)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should return an empty array when scaffold is valid" do
|
70
|
+
subject.expects(:scaffold).returns([@valid,@valid])
|
71
|
+
subject.errors.should be_empty
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return invalid entries when scaffold is invalid" do
|
75
|
+
subject.expects(:scaffold).returns([@invalid,@valid])
|
76
|
+
subject.errors.should == [@invalid]
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should ignore entries which are not sequences" do
|
80
|
+
@non_sequence = stub(:entry_type => :other)
|
81
|
+
subject.expects(:scaffold).returns([@invalid,@valid,@non_sequence])
|
82
|
+
subject.errors.should == [@invalid]
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "scaffold validation with no overlapping inserts" do
|
88
|
+
|
89
|
+
subject do
|
90
|
+
validate = described_class.new(mock_command_line_settings)
|
91
|
+
validate.stubs(:errors).returns([])
|
92
|
+
validate
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should not raise any errors" do
|
96
|
+
lambda{ subject.execute }.should_not raise_error
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should not return anything" do
|
100
|
+
subject.execute.should == nil
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "returning errors using the errors method" do
|
106
|
+
|
107
|
+
subject do
|
108
|
+
validate = described_class.new(mock_command_line_settings)
|
109
|
+
|
110
|
+
sequence = stub(:source => :seq1)
|
111
|
+
validate.stubs(:errors).returns([sequence])
|
112
|
+
described_class.stubs(:sequence_errors).with(sequence).returns(
|
113
|
+
[[stub(:open => 1,:close => 2, :source => 'some_insert')]])
|
114
|
+
|
115
|
+
validate
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should not raise an error" do
|
119
|
+
lambda{ subject.execute }.should_not raise_error
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should return a string" do
|
123
|
+
subject.execute.should be_instance_of(String)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should be an array when YAML is parsed" do
|
127
|
+
lambda{ YAML.load(subject.execute) }.should_not raise_error
|
128
|
+
YAML.load(subject.execute).should be_instance_of(Array)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "the attributes of the error data" do
|
134
|
+
|
135
|
+
subject do
|
136
|
+
validate = described_class.new(mock_command_line_settings)
|
137
|
+
@err = {:open => 1,:close => 2,:source => 'some_insert'}
|
138
|
+
|
139
|
+
sequence = stub(:source => 'seq1')
|
140
|
+
validate.stubs(:errors).returns([sequence])
|
141
|
+
described_class.stubs(:sequence_errors).with(sequence).returns([[stub(@err)]])
|
142
|
+
|
143
|
+
YAML.load(validate.execute).first['sequence-insert-overlap']
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should name each overlap entry 'sequence-insert-overlap'" do
|
147
|
+
subject.should_not be_nil
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should provide the name of overlap containing sequence" do
|
151
|
+
subject['source'].should == 'seq1'
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should provide an array of the overlapping inserts" do
|
155
|
+
subject['inserts'].should be_instance_of(Array)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should provide the coordinates of the overlapping inserts" do
|
159
|
+
error = subject['inserts'].first
|
160
|
+
error['open'].should == @err[:open]
|
161
|
+
error['close'].should == @err[:close]
|
162
|
+
error['source'].should == @err[:source]
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'..','spec_helper')
|
2
|
+
|
3
|
+
describe Scaffolder::ToolIndex do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@help_tool = Scaffolder::Tool::Help
|
7
|
+
|
8
|
+
@tool_class = Class.new(Scaffolder::Tool)
|
9
|
+
@tool_name = 'type'
|
10
|
+
Scaffolder::Tool.const_set(@tool_name.capitalize,@tool_class)
|
11
|
+
|
12
|
+
@args = OpenStruct.new({ :rest => %W|#{@tool_name} arg1 arg2| })
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:each) do
|
16
|
+
Scaffolder::Tool.send(:remove_const,'Type')
|
17
|
+
end
|
18
|
+
|
19
|
+
subject do
|
20
|
+
object = Object.new
|
21
|
+
object.extend described_class
|
22
|
+
object
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "tool_exists? method" do
|
26
|
+
|
27
|
+
it "should return false when no such tool exists" do
|
28
|
+
subject.tool_exists?('unknown-tool').should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return true when the tool exists" do
|
32
|
+
subject.tool_exists?(@tool_name).should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "get_tool method" do
|
38
|
+
|
39
|
+
it "should return nil when no such tool exists" do
|
40
|
+
subject.get_tool('unknown-tool').should be_nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return the tool class when the tool exists" do
|
44
|
+
subject.get_tool(@tool_name).should == @tool_class
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'..','spec_helper')
|
2
|
+
|
3
|
+
describe Scaffolder::Tool do
|
4
|
+
|
5
|
+
describe "initialisation with attributes" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@settings = mock_command_line_settings
|
9
|
+
end
|
10
|
+
|
11
|
+
subject do
|
12
|
+
Scaffolder::Tool.new(@settings)
|
13
|
+
end
|
14
|
+
|
15
|
+
its(:scaffold_file){ should == @settings.scaffold_file }
|
16
|
+
its(:sequence_file){ should == @settings.sequence_file }
|
17
|
+
its(:settings){ should == @settings }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "the run method" do
|
21
|
+
|
22
|
+
before(:all) do
|
23
|
+
@settings = mock_command_line_settings
|
24
|
+
end
|
25
|
+
|
26
|
+
before(:each) do
|
27
|
+
@message = "output\n"
|
28
|
+
@out = StringIO.new
|
29
|
+
@err = StringIO.new
|
30
|
+
end
|
31
|
+
|
32
|
+
subject do
|
33
|
+
Scaffolder::Tool.new(@settings)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should print to standard out when there are no errors" do
|
37
|
+
subject.expects(:execute).returns(@message)
|
38
|
+
lambda{ subject.run(@out,@err) }.should raise_error
|
39
|
+
@err.string.should == ""
|
40
|
+
@out.string.should == @message
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should print nothing to standard out when there no error and no output" do
|
44
|
+
subject.expects(:execute).returns(nil)
|
45
|
+
lambda{ subject.run(@out,@err) }.should raise_error
|
46
|
+
@err.string.should == ""
|
47
|
+
@out.string.should == ""
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should give a zero exit code when there are no errors" do
|
51
|
+
subject.expects(:execute).returns(@message)
|
52
|
+
lambda{ subject.run(@out,@err) }.should exit_with_code(0)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should print to standard error when there are errors" do
|
56
|
+
subject.expects(:execute).raises(Exception, @message)
|
57
|
+
lambda{ subject.run(@out,@err) }.should raise_error
|
58
|
+
@out.string.should == ""
|
59
|
+
@err.string.should == "Error. #{@message}"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should give a non-zero exit code when there are errors" do
|
63
|
+
subject.expects(:execute).raises(Exception, @message)
|
64
|
+
lambda{ subject.run(@out,@err) }.should exit_with_code(1)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "error checking with the scaffold method" do
|
70
|
+
|
71
|
+
before(:each) do
|
72
|
+
FakeFS.activate!
|
73
|
+
|
74
|
+
@scaffold_file = File.new('scaffold','w').path
|
75
|
+
File.open(@scaffold_file,'w'){|out| out.write "some_content" }
|
76
|
+
|
77
|
+
@sequence_file = File.new('sequence','w').path
|
78
|
+
File.open(@sequence_file,'w'){|out| out.write "some_content" }
|
79
|
+
|
80
|
+
@empty_file = File.new('empty_file','w').path
|
81
|
+
File.open(@empty_file,'w'){|out| out.write "" }
|
82
|
+
|
83
|
+
@missing_file = "file"
|
84
|
+
end
|
85
|
+
|
86
|
+
after(:each) do
|
87
|
+
FakeFS.deactivate!
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should raise an error if the sequence file is missing" do
|
91
|
+
settings = mock_command_line_settings(@scaffold_file,@missing_file)
|
92
|
+
tool = Scaffolder::Tool.new(settings)
|
93
|
+
lambda{ tool.scaffold }.should raise_error(ArgumentError,
|
94
|
+
"Sequence file not found: #{@missing_file}")
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should raise an error if the sequence file is empty" do
|
98
|
+
settings = mock_command_line_settings(@scaffold_file,@empty_file)
|
99
|
+
tool = Scaffolder::Tool.new(settings)
|
100
|
+
lambda{ tool.scaffold }.should raise_error(ArgumentError,
|
101
|
+
"Sequence file is empty: #{@empty_file}")
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should raise an error if the scaffold file is missing" do
|
105
|
+
settings = mock_command_line_settings(@missing_file,@sequence_file)
|
106
|
+
tool = Scaffolder::Tool.new(settings)
|
107
|
+
lambda{ tool.scaffold }.should raise_error(ArgumentError,
|
108
|
+
"Scaffold file not found: #{@missing_file}")
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should raise an error if the scaffold file is empty" do
|
112
|
+
settings = mock_command_line_settings(@empty_file,@sequence_file)
|
113
|
+
tool = Scaffolder::Tool.new(settings)
|
114
|
+
lambda{ tool.scaffold }.should raise_error(ArgumentError,
|
115
|
+
"Scaffold file is empty: #{@empty_file}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "creating the scaffold with the scaffold method" do
|
120
|
+
|
121
|
+
before(:each) do
|
122
|
+
entries = [{:name => 'seq1', :nucleotides => 'ATGC'}]
|
123
|
+
|
124
|
+
@scaffold_file = File.new('scaffold','w').path
|
125
|
+
@sequence_file = File.new('sequence','w').path
|
126
|
+
write_scaffold_file(entries,@scaffold_file)
|
127
|
+
write_sequence_file(entries,@sequence_file)
|
128
|
+
end
|
129
|
+
|
130
|
+
after(:each) do
|
131
|
+
File.delete @scaffold_file, @sequence_file
|
132
|
+
end
|
133
|
+
|
134
|
+
subject do
|
135
|
+
Scaffolder::Tool.new(
|
136
|
+
mock_command_line_settings(@scaffold_file,@sequence_file))
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should produce the expected sequence scaffold" do
|
140
|
+
subject.scaffold.length.should == 1
|
141
|
+
subject.scaffold.first.entry_type.should == :sequence
|
142
|
+
subject.scaffold.first.sequence.should == 'ATGC'
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'tempfile'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
require 'hashie'
|
8
|
+
require 'rspec'
|
9
|
+
require 'fakefs/safe'
|
10
|
+
require 'mocha'
|
11
|
+
require 'bio'
|
12
|
+
require 'scaffolder/test/helpers'
|
13
|
+
require 'scaffolder'
|
14
|
+
|
15
|
+
require 'scaffolder/tool'
|
16
|
+
require 'scaffolder/tool_index'
|
17
|
+
require 'scaffolder/binary_helper'
|
18
|
+
Dir["#{File.dirname(__FILE__)}/../lib/scaffolder/tool/*.rb"].each do |f|
|
19
|
+
require File.expand_path(f)
|
20
|
+
end
|
21
|
+
|
22
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each do |f|
|
23
|
+
require File.expand_path(f)
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
config.mock_with :mocha
|
28
|
+
|
29
|
+
include Scaffolder::Test::Helpers
|
30
|
+
|
31
|
+
def tool_subclasses
|
32
|
+
ObjectSpace.each_object.map{|obj| obj.class }.select do |cls|
|
33
|
+
cls.superclass == Scaffolder::Tool
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def mock_command_line_settings(scaf_file = mock, seq_file = mock, hash_args={})
|
38
|
+
settings = mock
|
39
|
+
|
40
|
+
settings.stubs(:rest).returns([scaf_file,seq_file])
|
41
|
+
settings.stubs(:sequence_file).returns(seq_file)
|
42
|
+
settings.stubs(:scaffold_file).returns(scaf_file)
|
43
|
+
|
44
|
+
hash_args.each do |key,value|
|
45
|
+
settings.expects(:[]).with(key).returns(value)
|
46
|
+
end
|
47
|
+
|
48
|
+
settings
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# See: How can I validate exits and aborts in RSpec?
|
2
|
+
# http://stackoverflow.com/questions/1480537
|
3
|
+
|
4
|
+
module ExitCodeMatchers
|
5
|
+
RSpec::Matchers.define :exit_with_code do |code|
|
6
|
+
actual = nil
|
7
|
+
match do |block|
|
8
|
+
begin
|
9
|
+
block.call
|
10
|
+
rescue SystemExit => e
|
11
|
+
actual = e.status
|
12
|
+
end
|
13
|
+
actual and actual == code
|
14
|
+
end
|
15
|
+
failure_message_for_should do |block|
|
16
|
+
"expected block to call exit(#{code}) but exit" +
|
17
|
+
(actual.nil? ? " not called" : "(#{actual}) was called")
|
18
|
+
end
|
19
|
+
failure_message_for_should_not do |block|
|
20
|
+
"expected block not to call exit(#{code})"
|
21
|
+
end
|
22
|
+
description do
|
23
|
+
"expect block to call exit(#{code})"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|