pants 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +1 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +42 -0
- data/History.rdoc +6 -0
- data/README.rdoc +339 -0
- data/Rakefile +23 -0
- data/bin/pants +59 -0
- data/lib/pants.rb +84 -0
- data/lib/pants/core.rb +216 -0
- data/lib/pants/error.rb +5 -0
- data/lib/pants/logger.rb +8 -0
- data/lib/pants/network_helpers.rb +25 -0
- data/lib/pants/readers/base_reader.rb +302 -0
- data/lib/pants/readers/file_reader.rb +81 -0
- data/lib/pants/readers/udp_reader.rb +80 -0
- data/lib/pants/seam.rb +120 -0
- data/lib/pants/version.rb +3 -0
- data/lib/pants/writers/base_writer.rb +73 -0
- data/lib/pants/writers/file_writer.rb +59 -0
- data/lib/pants/writers/udp_writer.rb +125 -0
- data/pants.gemspec +31 -0
- data/spec/acceptance/file_reads_spec.rb +24 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/matchers/file_same_size_as.rb +20 -0
- data/spec/support/pants.wav +0 -0
- data/spec/unit/pants/core_spec.rb +105 -0
- data/spec/unit/pants/readers/base_reader_spec.rb +340 -0
- data/spec/unit/pants/readers/file_reader_spec.rb +94 -0
- data/spec/unit/pants/version_spec.rb +7 -0
- data/spec/unit/pants/writers/base_writer_spec.rb +35 -0
- data/spec/unit/pants/writers/file_writer_spec.rb +71 -0
- data/spec/unit/pants/writers/udp_writer_spec.rb +146 -0
- data/spec/unit/pants_spec.rb +6 -0
- data/tasks/pantsmark.thor +66 -0
- data/test_readers.rb +43 -0
- data/test_seams.rb +107 -0
- metadata +215 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pants/readers/file_reader'
|
3
|
+
|
4
|
+
|
5
|
+
describe Pants::Readers::FileReader do
|
6
|
+
let(:core_stopper_callback) do
|
7
|
+
double "EventMachine.Callback", call: true
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:tick_loop) do
|
11
|
+
tl = double "EventMachine::TickLoop"
|
12
|
+
tl.stub(:on_stop)
|
13
|
+
|
14
|
+
tl
|
15
|
+
end
|
16
|
+
|
17
|
+
before do
|
18
|
+
EventMachine.stub(:tick_loop).and_return(tick_loop)
|
19
|
+
EventMachine::Iterator.stub_chain(:new, :each)
|
20
|
+
end
|
21
|
+
|
22
|
+
subject do
|
23
|
+
Pants::Readers::FileReader.new('some file', core_stopper_callback)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#start" do
|
27
|
+
let(:file) { double "File" }
|
28
|
+
let(:starter) { double "EventMachine::Callback starter" }
|
29
|
+
let(:stopper) { double "EventMachine::Callback stopper" }
|
30
|
+
|
31
|
+
before do
|
32
|
+
File.should_receive(:open).and_return(file)
|
33
|
+
subject.should_receive(:starter).and_return(starter)
|
34
|
+
subject.should_receive(:stopper).and_return(stopper)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "defines an EventMachine Callback" do
|
38
|
+
EventMachine.should_receive(:Callback).times.and_yield.and_call_original
|
39
|
+
|
40
|
+
EventMachine.should_receive(:attach) do |arg1, arg2, arg3, arg4, arg5|
|
41
|
+
arg1.should == file
|
42
|
+
arg2.should == Pants::Readers::FileReaderConnection
|
43
|
+
arg3.should be_a EventMachine::Channel
|
44
|
+
arg4.should == starter
|
45
|
+
arg5.should == stopper
|
46
|
+
end
|
47
|
+
|
48
|
+
subject.start
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe Pants::Readers::FileReaderConnection do
|
54
|
+
let(:channel) { double "EventMachine::Channel" }
|
55
|
+
let(:starter) { double "EventMachine::Callback starter" }
|
56
|
+
let(:stopper) { double "EventMachine::Callback stopper" }
|
57
|
+
|
58
|
+
subject do
|
59
|
+
Pants::Readers::FileReaderConnection.new(1, channel, starter, stopper)
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#post_init" do
|
63
|
+
let(:starter) { double "EventMachine::Callback" }
|
64
|
+
|
65
|
+
it "tells the starter that it's started" do
|
66
|
+
starter.should_receive(:call)
|
67
|
+
subject
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#receive_data" do
|
72
|
+
let(:data) { "some data" }
|
73
|
+
|
74
|
+
before do
|
75
|
+
Pants::Readers::FileReaderConnection.any_instance.stub(:post_init)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "directly writes it to the channel" do
|
79
|
+
channel.should_receive(:<<).with(data)
|
80
|
+
subject.receive_data(data)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#unbind" do
|
85
|
+
before do
|
86
|
+
Pants::Readers::FileReaderConnection.any_instance.stub(:post_init)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "tells the stopper that it's stopped" do
|
90
|
+
stopper.should_receive(:call)
|
91
|
+
subject.unbind
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pants/writers/base_writer'
|
3
|
+
|
4
|
+
|
5
|
+
describe Pants::Writers::BaseWriter do
|
6
|
+
let(:channel) { double "EventMachine::Channel" }
|
7
|
+
|
8
|
+
subject do
|
9
|
+
Pants::Writers::BaseWriter.new(channel)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#starter" do
|
13
|
+
context "@starter not yet defined" do
|
14
|
+
it "creates a new deferrable that sets @running to true" do
|
15
|
+
subject.should_not be_running
|
16
|
+
subject.starter.call
|
17
|
+
subject.should be_running
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#stopper" do
|
23
|
+
context "@stopper not yet defined and @running is true" do
|
24
|
+
before do
|
25
|
+
subject.instance_variable_set(:@running, true)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "creates a new deferrable that sets @running to false" do
|
29
|
+
subject.should be_running
|
30
|
+
subject.stopper.call
|
31
|
+
subject.should_not be_running
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pants/writers/file_writer'
|
3
|
+
|
4
|
+
|
5
|
+
describe Pants::Writers::FileWriter do
|
6
|
+
let(:channel) { EventMachine::Channel.new }
|
7
|
+
|
8
|
+
subject do
|
9
|
+
Pants::Writers::FileWriter.new(file, channel)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#stop" do
|
13
|
+
let(:file) do
|
14
|
+
c = double "File"
|
15
|
+
c.should_receive(:closed?).and_return(false)
|
16
|
+
c.should_receive(:close)
|
17
|
+
|
18
|
+
c
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:stopper) do
|
22
|
+
s = double "EventMachine::Callback"
|
23
|
+
s.should_receive(:call)
|
24
|
+
|
25
|
+
s
|
26
|
+
end
|
27
|
+
|
28
|
+
before do
|
29
|
+
File.stub(:open)
|
30
|
+
subject.instance_variable_set(:@file, file)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "closes the file and calls succeed on the stopper" do
|
34
|
+
subject.should_receive(:stopper).and_return(stopper)
|
35
|
+
subject.stop
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#start" do
|
40
|
+
let(:data) do
|
41
|
+
'0' * 100
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:file) do
|
45
|
+
f = double "File"
|
46
|
+
f.should_receive(:write_nonblock).once.with(data).and_return(data.size)
|
47
|
+
f.should_receive(:closed?).and_return(true)
|
48
|
+
|
49
|
+
f
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:tick_loop) do
|
53
|
+
t = double "EventMachine::TickLoop"
|
54
|
+
t.should_receive(:on_stop).and_yield
|
55
|
+
|
56
|
+
t
|
57
|
+
end
|
58
|
+
|
59
|
+
before do
|
60
|
+
File.should_receive(:open).and_return(file)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "subscribes to the channel and writes data as it comes in" do
|
64
|
+
EM.should_receive(:defer).and_yield
|
65
|
+
channel.should_receive(:subscribe).and_yield(data).and_call_original
|
66
|
+
EventMachine.should_receive(:tick_loop).and_yield.and_return(tick_loop)
|
67
|
+
|
68
|
+
subject.start
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pants/writers/udp_writer'
|
3
|
+
|
4
|
+
|
5
|
+
describe Pants::Writers::UDPWriter do
|
6
|
+
let(:channel) { double "EventMachine::Channel" }
|
7
|
+
let(:ip) { '127.0.0.1' }
|
8
|
+
let(:port) { 1234 }
|
9
|
+
before { subject.stub(:log) }
|
10
|
+
|
11
|
+
subject do
|
12
|
+
Pants::Writers::UDPWriter.new(ip, port, channel)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#start" do
|
16
|
+
let(:starter) do
|
17
|
+
s = double "EventMachine::Callback"
|
18
|
+
s.should_receive(:call)
|
19
|
+
|
20
|
+
s
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:tick_loop) do
|
24
|
+
t = double "EventMachine::TickLoop"
|
25
|
+
t.should_receive(:on_stop).and_yield
|
26
|
+
|
27
|
+
t
|
28
|
+
end
|
29
|
+
|
30
|
+
before do
|
31
|
+
subject.stub(:starter).and_return(starter)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "opens a datagram socket on 0.0.0.0 then calls succeed on the starter" do
|
35
|
+
EventMachine.should_receive(:defer).and_yield
|
36
|
+
EventMachine.should_receive(:open_datagram_socket).with(
|
37
|
+
'0.0.0.0', 0, Pants::Writers::UDPWriterConnection, channel, ip, port
|
38
|
+
).and_return(true)
|
39
|
+
EventMachine.should_receive(:tick_loop).and_yield.and_return(tick_loop)
|
40
|
+
|
41
|
+
subject.start
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#stop" do
|
46
|
+
let(:connection) do
|
47
|
+
c = double "EventMachine::Connection"
|
48
|
+
c.should_receive(:close_connection_after_writing)
|
49
|
+
|
50
|
+
c
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:stopper) do
|
54
|
+
s = double "EventMachine::Callback"
|
55
|
+
s.should_receive(:call)
|
56
|
+
|
57
|
+
s
|
58
|
+
end
|
59
|
+
|
60
|
+
before do
|
61
|
+
subject.instance_variable_set(:@connection, connection)
|
62
|
+
subject.stub(:stopper).and_return(stopper)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "closes the socket and calls succeed on the stopper" do
|
66
|
+
subject.stop
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe Pants::Writers::UDPWriterConnection do
|
72
|
+
let(:channel) { EventMachine::Channel.new }
|
73
|
+
let(:port) { 1234 }
|
74
|
+
let(:ip) { '127.0.0.1' }
|
75
|
+
|
76
|
+
subject do
|
77
|
+
Pants::Writers::UDPWriterConnection.new(1, channel, ip, port)
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#initialize" do
|
81
|
+
before do
|
82
|
+
Pants::Writers::UDPWriterConnection.any_instance.stub(:post_init)
|
83
|
+
end
|
84
|
+
|
85
|
+
context "multicast IP address" do
|
86
|
+
let(:ip) { '224.0.0.1' }
|
87
|
+
|
88
|
+
it "sets up the socket to do multicast" do
|
89
|
+
Pants::Writers::UDPWriterConnection.any_instance.
|
90
|
+
should_receive(:setup_multicast_socket).with(ip)
|
91
|
+
|
92
|
+
Pants::Writers::UDPWriterConnection.new(0, channel, ip, port)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "unicast IP address" do
|
97
|
+
let(:ip) { '223.0.0.1' }
|
98
|
+
|
99
|
+
it "does not set up the socket to do multicast" do
|
100
|
+
Pants::Writers::UDPWriterConnection.any_instance.
|
101
|
+
should_not_receive(:setup_multicast_socket)
|
102
|
+
|
103
|
+
Pants::Writers::UDPWriterConnection.new(0, channel, ip, port)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#post_init" do
|
109
|
+
around(:each) do |example|
|
110
|
+
EM.run do
|
111
|
+
example.run
|
112
|
+
EM.stop
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
before do
|
117
|
+
channel.should_receive(:subscribe).and_yield(data).and_call_original
|
118
|
+
end
|
119
|
+
|
120
|
+
context "data is bigger than PACKET_SPLIT_THRESHOLD" do
|
121
|
+
let(:data) do
|
122
|
+
"0" * Pants::Writers::UDPWriterConnection::PACKET_SPLIT_THRESHOLD * split_count
|
123
|
+
end
|
124
|
+
|
125
|
+
let(:split_count) { 3 }
|
126
|
+
|
127
|
+
it "splits the data by PACKET_SPLIT_SIZE and sends each chunk" do
|
128
|
+
subject.should_receive(:send_datagram).exactly(split_count + 1).times
|
129
|
+
channel << data
|
130
|
+
subject
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "data is smaller than PACKET_SPLIT_THRESHOLD" do
|
135
|
+
let(:data) do
|
136
|
+
"0" * 100
|
137
|
+
end
|
138
|
+
|
139
|
+
it "sends the data as it is" do
|
140
|
+
subject.should_receive(:send_datagram).once.with(data, ip, port)
|
141
|
+
channel << data
|
142
|
+
subject
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require 'benchmark'
|
5
|
+
require './lib/pants'
|
6
|
+
require 'bundler/setup'
|
7
|
+
|
8
|
+
|
9
|
+
class Pantsmark < Thor
|
10
|
+
desc "file_copy [FILE]", "Copies [FILE] --times number of times and benchmarks it"
|
11
|
+
method_option :times, type: :numeric, :default => 100
|
12
|
+
def file_copy(file_path)
|
13
|
+
Benchmark.bm do |x|
|
14
|
+
Pants.log = false
|
15
|
+
|
16
|
+
x.report("\tpants:") do
|
17
|
+
Pants.read(file_path) do |tee|
|
18
|
+
options[:times].times do |i|
|
19
|
+
tee.write_to("pants_test#{i}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
options[:times].times do |i|
|
25
|
+
result = %x[diff "#{file_path}" pants_test#{i}]
|
26
|
+
puts "Diff result: #{result}" unless result.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
x.report(" FileUtils.cp:") do
|
30
|
+
threads = []
|
31
|
+
|
32
|
+
options[:times].times do |i|
|
33
|
+
threads << Thread.new do
|
34
|
+
FileUtils.cp('pants_test1', "fu_cp_test#{i}")
|
35
|
+
end
|
36
|
+
|
37
|
+
threads.last.join
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
x.report(" cp:") do
|
42
|
+
threads = []
|
43
|
+
|
44
|
+
options[:times].times do |i|
|
45
|
+
threads << Thread.new do
|
46
|
+
`cp pants_test1 cp_test#{i}`
|
47
|
+
end
|
48
|
+
|
49
|
+
threads.last.join
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue
|
54
|
+
FileUtils.rm_rf(Dir["./pants_test*"])
|
55
|
+
FileUtils.rm_rf(Dir["./fu_cp_test*"])
|
56
|
+
FileUtils.rm_rf(Dir["./cp_test*"])
|
57
|
+
|
58
|
+
raise
|
59
|
+
ensure
|
60
|
+
FileUtils.rm_rf(Dir["./pants_test*"])
|
61
|
+
FileUtils.rm_rf(Dir["./fu_cp_test*"])
|
62
|
+
FileUtils.rm_rf(Dir["./cp_test*"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
Pantsmark.start
|
data/test_readers.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative './lib/pants'
|
2
|
+
|
3
|
+
|
4
|
+
Pants.log = true
|
5
|
+
|
6
|
+
#EM.threadpool_size = 200
|
7
|
+
#EM.kqueue
|
8
|
+
#EM.epoll
|
9
|
+
|
10
|
+
orig_file = 'spec/support/pants.wav'
|
11
|
+
dest_file = 'dest_test_file'
|
12
|
+
#Pants.read('udp://127.0.0.1:1234') do |reader|
|
13
|
+
Pants.read(orig_file) do |reader|
|
14
|
+
10.times do |i|
|
15
|
+
reader.add_writer(dest_file)
|
16
|
+
reader.add_writer("udp://127.0.0.1:#{1235 + i}")
|
17
|
+
end
|
18
|
+
reader.add_writer('udp://10.221.222.90:9000')
|
19
|
+
end
|
20
|
+
|
21
|
+
if defined? orig_file
|
22
|
+
pants_file_size = File.stat(dest_file).size
|
23
|
+
orig_file_size = File.stat(orig_file).size
|
24
|
+
puts "Pants file size: #{pants_file_size}"
|
25
|
+
puts "Original file size: #{orig_file_size}"
|
26
|
+
puts "Difference: #{orig_file_size - pants_file_size}"
|
27
|
+
end
|
28
|
+
|
29
|
+
#==============
|
30
|
+
core = Pants::Core.new
|
31
|
+
udp_reader = core.read 'udp://127.0.0.1:1234'
|
32
|
+
udp_reader.write_to 'udp://127.0.0.1:1235'
|
33
|
+
|
34
|
+
udp_reader2 = core.add_reader(Pants::Readers::UDPReader, '127.0.0.1', 1234)
|
35
|
+
udp_reader2.add_writer(Pants::Writers::UDPWriter, '127.0.0.1', 1235)
|
36
|
+
|
37
|
+
udp_reader3 = Pants::Readers::UDPReader.new('127.0.0.1', 1234, core.callback)
|
38
|
+
udp_writer3 = Pants::Writers::UDPWriter.new('127.0.0.1', 1235, udp_reader3.write_to_channel)
|
39
|
+
core.add_reader(udp_reader3)
|
40
|
+
udp_reader3.add_writer(udp_writer3)
|
41
|
+
|
42
|
+
seam = udp_reader.add_seam(Pants::SimpleSeam)
|
43
|
+
seam.write_to 'my_file'
|