ttcp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+
6
+ # mac noise
7
+ .DS_Store
8
+
9
+ # ruby mine noise
10
+ .idea/
11
+
12
+ # ignore ci_reporter output from rspec tests:
13
+ spec/reports
@@ -0,0 +1 @@
1
+ compat.version=1.9
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ttcp-rb.gemspec
4
+ gemspec
5
+
6
+ # optional dependencies (not included in gemspec):
7
+ group :development, :test do
8
+ gem 'guard'
9
+ gem 'guard-rspec'
10
+ end
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec, :cli => '--color --format doc' do
5
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ Copyright (c) 2012 Matthew Connolly
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
@@ -0,0 +1,69 @@
1
+ # ttcp (Test TCP) - Benchmarking Tool and Simple Network Traffic Generator
2
+
3
+ This is a ruby version of the original C ttcp program.
4
+
5
+ This tool aims to be a drop in replacement for the original C version, with interoperable networking and command line options. ie: you can have the C and ruby versions talk to each other.
6
+
7
+ http://www.pcausa.com/Utilities/pcattcp.htm
8
+
9
+ ## Installation
10
+
11
+ Run `$ gem install ttcp`.
12
+
13
+ ## Usage
14
+
15
+ Similar to the original TTCP program, run a receiver on one machine and a transmitter on another, in that order:
16
+ ```machine1$ ttcp -r [-p <port>]
17
+ ```
18
+
19
+ And:
20
+ ```machine2$ ttcp -t <machine1_ip_address> [-p port]
21
+ ```
22
+
23
+ For more command line options run:
24
+ ```machine1$ ttcp --help
25
+ ```
26
+
27
+ ## Dependencies
28
+
29
+ Runtime dependencies:
30
+
31
+ * only ruby std libraries
32
+
33
+ Development dependencies:
34
+
35
+ * bundler
36
+ * rake
37
+ * rpsec
38
+ * ci_reporter (for use with jenkins)
39
+
40
+ Optionally:
41
+
42
+ * guard
43
+ * guard-rspec
44
+
45
+ ## Compatibility
46
+
47
+ * MRI ruby 1.8.7
48
+ * MRI ruby 1.9.2
49
+ * JRuby (tested in 1.9 compatibility mode)
50
+
51
+ ## Contributing
52
+
53
+ 1. Fork
54
+ 2. Install dependencies by running `$ bundle install`
55
+ 3. Write tests and code
56
+ 4. Make sure the tests pass by running `$ rake test`
57
+ 5. Push and send a pull request on GitHub
58
+
59
+ ## Known issues
60
+
61
+ * Tests don't seem to run in JRuby 1.6.5, but the TTCP program works itself.
62
+
63
+ ## Credits
64
+
65
+ Special thanks to the Mike Muuss and Terry Slattery and other contributors of the original TTCP program.
66
+
67
+ ## Copyright
68
+
69
+ Copyright © 2012 Matt Connolly. Released under the MIT license. See LICENSE.
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ require 'ci/reporter/rake/rspec'
5
+
6
+ RSpec::Core::RakeTask.new(:test => ["ci:setup:rspec"]) do |t|
7
+ t.pattern = '**/*_spec.rb'
8
+ end
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # ttcp
4
+ # ©2012 Matt Connolly
5
+ #
6
+ # This is a ruby implementation of the TTCP network test program.
7
+ #
8
+
9
+
10
+
11
+ require 'optparse'
12
+ require File.expand_path('../lib/ttcp', File.dirname(__FILE__))
13
+
14
+ =begin
15
+ Usage: ttcp -t [-options] host [ < in ]\n\
16
+ ttcp -r [-options > out]\n\
17
+ Common options:\n\
18
+ -l ## length of bufs read from or written to network (default 8192)\n\
19
+ -u use UDP instead of TCP\n\
20
+ -p ## port number to send to or listen at (default 5001)\n\
21
+ -s -t: source a pattern to network\n\
22
+ -r: sink (discard) all data from network\n\
23
+ -A align the start of buffers to this modulus (default 16384)\n\
24
+ -O start buffers at this offset from the modulus (default 0)\n\
25
+ -v verbose: print more statistics\n\
26
+ -d set SO_DEBUG socket option\n\
27
+ -b ## set socket buffer size (if supported)\n\
28
+ -f X format for rate: k,K = kilo{bit,byte}; m,M = mega; g,G = giga\n\
29
+ Options specific to -t:\n\
30
+ -n## number of source bufs written to network (default 2048)\n\
31
+ -D don't buffer TCP writes (sets TCP_NODELAY socket option)\n\
32
+ Options specific to -r:\n\
33
+ -B for -s, only output full blocks as specified by -l (for TAR)\n\
34
+ -T \"touch\": access each byte as it's read\n\
35
+ =end
36
+
37
+ #
38
+ # defaults:
39
+ #
40
+
41
+ options = TTCP::TTCP.default_options
42
+
43
+ optparse = OptionParser.new do |opts|
44
+ opts.banner = <<END
45
+ Usage: ttcp.rb -t [options] host [ < in ]
46
+ ttcp.rb -r [options > out]
47
+ END
48
+
49
+ opts.separator "Must choose one of:"
50
+ opts.on('-t', '--transmit', "Transmit data to another TTCP program") do
51
+ options[:transmit] = true
52
+ end
53
+
54
+ opts.on('-r', '--receive', "Receive data from another TTCP program") do
55
+ options[:receive] = true
56
+ end
57
+
58
+ opts.separator ""
59
+ opts.separator "Other options:"
60
+ opts.on('-l', '--length NUM', "Length of bufs read from or written to network (default #{options[:length]})") do |length|
61
+ options[:length] = length.to_i if length.to_i > 0
62
+ end
63
+
64
+ opts.on('-u', '--udp', 'Use UDP instead of default TCP') do
65
+ options[:udp] = true
66
+ options[:tcp] = false
67
+ end
68
+
69
+
70
+ opts.on('-s', '--sink',
71
+ "When transmitting, source pattern to network.",
72
+ "When receiving, discard all incoming data") do
73
+ options[:sink] = !options[:sink]
74
+ end
75
+
76
+ opts.on('-p', '--port PORT', "Receive on port PORT / Transmit to remote port PORT") do |port|
77
+ options[:port] = port.to_i if port.to_i > 0
78
+ end
79
+
80
+ opts.on('-f', '--format F', "Format: K = K bytes, M = M bytes, G = G Bytes, k = K bits, m = M bits, g = G bits. Default = K") do |fmt|
81
+ options[:format] = fmt
82
+ end
83
+
84
+ opts.on('-v', '--verbose', "Verbose output") { options[:verbose] = true }
85
+ opts.on('-T', '--touch', "Touch (access) all incoming data") { options[:touch] = true }
86
+
87
+ opts.on('--version', "Output the version of TTCP") do
88
+ puts TTCP::VERSION
89
+ exit(0)
90
+ end
91
+
92
+ opts.on('-n', '--numbufs NUM', "Set number of buffers to send / receive (default = #{options[:num_buffers]})") do |num|
93
+ options[:num_buffers] = num.to_i if num.to_i > 0
94
+ end
95
+
96
+ opts.on_tail("-h", "--help", "Show this message") do
97
+ puts opts
98
+ exit(0)
99
+ end
100
+
101
+
102
+
103
+ end
104
+
105
+
106
+ begin
107
+ optparse.parse!
108
+
109
+ if options[:transmit]
110
+ #we require one argument: the host to connect to.
111
+ if ARGV.length < 1
112
+ raise "Transmit option requires a command line argument specifying the host to connect and transmit to."
113
+ end
114
+ options[:host] = ARGV[0]
115
+ end
116
+
117
+ unless options[:transmit] || options[:receive]
118
+ raise "You must select either transmit or receive option"
119
+ end
120
+
121
+ if options[:transmit] && options[:receive]
122
+ raise "You cannot select both transmit and receive options."
123
+ end
124
+
125
+ rescue SystemExit
126
+ # let it go through, we've been told to exit
127
+ raise
128
+ rescue Exception => x
129
+ $stderr.puts optparse
130
+ $stderr.puts x
131
+ exit(1)
132
+ end
133
+
134
+ exit(0) if ENV['DRY_RUN']
135
+
136
+ ttcp = TTCP::TTCP.new options
137
+ ttcp.run
138
+
@@ -0,0 +1,321 @@
1
+ require File.expand_path("ttcp/version", File.dirname(__FILE__))
2
+ require 'socket'
3
+
4
+ module TTCP
5
+
6
+ DEFAULT_OPTIONS = {
7
+ :transmit => false,
8
+ :receive => false,
9
+ :length => 8192,
10
+ :udp => false,
11
+ :tcp => true,
12
+ :port => 5001,
13
+ :verbose => false,
14
+ :socket_debug => false,
15
+ :format_rate => 'm',
16
+ :num_buffers => 2048,
17
+ :touch => false,
18
+ :sink => true,
19
+ :format => 'K',
20
+ }
21
+
22
+ def default_options
23
+ DEFAULT_OPTIONS
24
+ end
25
+
26
+
27
+ class TTCP
28
+
29
+ attr_reader :options
30
+ attr_reader :bytes_received
31
+ attr_accessor :stdout, :stderr
32
+
33
+ def self.default_options
34
+ DEFAULT_OPTIONS
35
+ end
36
+
37
+ #
38
+ # Create a TTCP test program instance. All configuration is done via the
39
+ # options hash passed in here.
40
+ #
41
+ def initialize(options = {})
42
+ @options = TTCP.default_options.merge options
43
+
44
+ if @options[:udp]
45
+ # enforce buffer length to be more than udp sentinel size.
46
+ @options[:length] = [@options[:length], 5].max
47
+ end
48
+
49
+ @bytes_received = 0
50
+
51
+ # by default, use the stdout and stderr.
52
+ @stdout = $stdout
53
+ @stderr = $stderr
54
+
55
+ end
56
+
57
+ #
58
+ # Run the TTCP test program according to the options specified with the .new call
59
+ #
60
+ def run
61
+ begin
62
+ num_bytes = 0
63
+ num_calls = 0
64
+
65
+ if @options[:transmit]
66
+ message "buflen=%d, nbuf=%d, remote port=%d" % [@options[:length], @options[:num_buffers], @options[:port]]
67
+
68
+ buf = source_buffer
69
+
70
+ @start_time = Time.now
71
+ @start_cpu_time = get_cpu_time
72
+
73
+ socket.write "ttcp" if @options[:udp]
74
+ @options[:num_buffers].times do
75
+ socket.write buf
76
+ num_bytes += buf.length
77
+ num_calls += 1
78
+ end
79
+ socket.write "ttcp" if @options[:udp]
80
+
81
+ elsif @options[:receive]
82
+ message "buflen=%d, nbuf=%d, local port=%d" % [@options[:length], @options[:num_buffers], @options[:port]]
83
+
84
+ receiving = true
85
+ sentinel_count = 0
86
+
87
+ if @options[:tcp]
88
+ receiving_socket = socket.accept
89
+ remote_address =
90
+ if receiving_socket.respond_to? :remote_address
91
+ "#{receiving_socket.remote_address.ip_address}:#{receiving_socket.remote_address.ip_port}"
92
+ elsif receiving_socket.respond_to? :peeraddr # this is what JRuby 1.6.5 has instead
93
+ "#{receiving_socket.peeraddr[2]}:#{receiving_socket.peeraddr[1]}"
94
+ else
95
+ "<Unknown>"
96
+ end
97
+ message("accept from #{remote_address}")
98
+ else
99
+ receiving_socket = socket
100
+ end
101
+
102
+ @start_time = Time.now
103
+ @start_cpu_time = get_cpu_time
104
+
105
+ while receiving
106
+
107
+ buf = receiving_socket.recv @options[:length]
108
+ num_bytes += buf.length unless buf.nil?
109
+ num_calls += 1
110
+
111
+ if buf.nil? || (@options[:tcp] && buf.length == 0)
112
+ receiving = false
113
+ else
114
+ if @options[:udp]
115
+ if buf.length <= 4
116
+ sentinel_count += 1
117
+ if sentinel_count >= 2
118
+ receiving = false
119
+ end
120
+ else
121
+ sink buf
122
+ touch buf if @options[:touch]
123
+ @bytes_received += buf.length
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ if @options[:tcp]
130
+ receiving_socket.close unless receiving_socket.closed?
131
+ receiving_socket = nil
132
+ end
133
+
134
+ end
135
+
136
+ ensure
137
+ @finish_time = Time.now
138
+ @finish_cpu_time = get_cpu_time
139
+ close
140
+ end
141
+
142
+ message("%d bytes in %.3f real seconds = %s/sec +++" % [num_bytes, duration, format_rate(num_bytes, duration)])
143
+
144
+ end
145
+
146
+
147
+ def duration
148
+ @finish_time - @start_time if @finish_time
149
+ end
150
+
151
+ def cpu_duration
152
+ @finish_cpu_time - @start_cpu_time if @finish_cpu_time
153
+ end
154
+
155
+ #
156
+ # close any sockets
157
+ #
158
+ def close
159
+ unless @socket.nil?
160
+ begin
161
+ @socket.shutdown Socket::SHUT_RDWR
162
+ rescue
163
+ # ignore any errors closing the socket
164
+ ensure
165
+ @socket = nil
166
+ end
167
+ end
168
+ end
169
+
170
+
171
+ #
172
+ # set the stdout to point to nothing
173
+ #
174
+ def stdout_to_null
175
+ @stdout = File.open("/dev/null", "w")
176
+ @stderr = File.open("/dev/null", "w")
177
+ end
178
+
179
+ private
180
+
181
+ # get the socket to be used for this ttcp run
182
+ def socket
183
+
184
+ if @socket.nil?
185
+
186
+ state = "socket"
187
+
188
+ begin
189
+ if @options[:transmit]
190
+
191
+ raise "Host not specified" unless @options[:host]
192
+
193
+ # create a socket to transmit to
194
+ if @options[:udp]
195
+ @socket = UDPSocket.new
196
+ message("socket")
197
+ state = "connect"
198
+ @socket.connect(@options[:host], @options[:port])
199
+ message("connect")
200
+ else
201
+ @socket = TCPSocket.new(@options[:host], @options[:port])
202
+ message("socket")
203
+ message("connect")
204
+ end
205
+
206
+ elsif @options[:receive]
207
+
208
+ # create a socket to receive from
209
+ if @options[:udp]
210
+ @socket = UDPSocket.new
211
+ message("socket")
212
+ state = "bind"
213
+ @socket.bind(@options[:host], @options[:port])
214
+ message("bind")
215
+ else
216
+ # create a TCPServer object
217
+ args = []
218
+ args << @options[:host] if @options[:host]
219
+ args << @options[:port]
220
+ @socket = TCPServer.new *args
221
+ message("socket")
222
+ state = "listen"
223
+ @socket.listen 0
224
+ message("listen")
225
+ end
226
+
227
+ else
228
+ raise "TTCP must be configured to transmit or receive"
229
+ end
230
+
231
+ rescue Exception => e
232
+ error(state, e)
233
+ end
234
+ end
235
+
236
+ @socket
237
+ end
238
+
239
+
240
+
241
+ def source_buffer
242
+ if @options[:sink]
243
+ # source for sink is random data
244
+ if RUBY_VERSION >= "1.9"
245
+ Random.new.bytes @options[:length]
246
+ else
247
+ source = ""
248
+ @options[:length].times { source << rand(255) }
249
+ source
250
+ end
251
+ else
252
+ # source for buffer is stdin
253
+ $stdin.read @options[:length]
254
+ end
255
+ end
256
+
257
+
258
+ #
259
+ # send the received buffer to the appropriate place:stdout or the sink (null)
260
+ def sink(buffer)
261
+ if @options[:sink]
262
+ #nothing, the sink
263
+ else
264
+ puts buffer
265
+ end
266
+ end
267
+
268
+
269
+ #
270
+ # read all the data in the buffer
271
+ #
272
+ def touch(buffer)
273
+ sum = 0
274
+ buffer.each_byte { |x| sum += x }
275
+ end
276
+
277
+
278
+ #
279
+ # print output, like the `mes()` function in the original C ttcp program.
280
+ #
281
+ def message(str)
282
+ mode = @options[:transmit] ? "t" : "r"
283
+ @stdout.puts "ttcp-#{mode}: #{str}"
284
+ end
285
+
286
+ #
287
+ # print output to stderr, like the `err()` function in the original C ttcp program.
288
+ # instead of exiting, raise an exception and let the caller exit. If no exception is passed,
289
+ # the error message is raised.
290
+ def error(str, exception=nil)
291
+ mode = @options[:transmit] ? "t" : "r"
292
+ @stderr.puts "ttcp-#{mode}: #{str}"
293
+ raise exception || str
294
+ end
295
+
296
+ # return combined user and system cpu time used by this process so far.
297
+ def get_cpu_time
298
+ tms = Process.times
299
+ tms.utime + tms.stime
300
+ end
301
+
302
+
303
+ UNIT_FACTOR = {
304
+ :k => [8 * 1024, 'Kbit'],
305
+ :m => [8 * 1024**2, 'Mbit'],
306
+ :g => [8 * 1024**3, 'Gbit'],
307
+ :K => [1024, 'K'],
308
+ :M => [1024**2, 'M'],
309
+ :G => [1024**3, 'G']
310
+ }
311
+
312
+ # genrate a string representation of the date, according to the @options[:format] setting
313
+ def format_rate(bytes, duration)
314
+ d = [0.001, duration].max
315
+ rate = bytes / d
316
+ factor, unit = UNIT_FACTOR[@options[:format].to_sym]
317
+ rate /= factor
318
+ "%0.3f %s" % [rate, unit]
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,3 @@
1
+ module TTCP
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,48 @@
1
+ require 'ttcp'
2
+ require 'ttcp/version'
3
+ require 'socket'
4
+
5
+
6
+ class Class
7
+ def publicize_methods
8
+ saved_private_instance_methods = self.private_instance_methods
9
+ self.class_eval { public *saved_private_instance_methods }
10
+ yield
11
+ self.class_eval { private *saved_private_instance_methods }
12
+ end
13
+ end
14
+
15
+
16
+ class ThreadSocket
17
+
18
+ attr_reader :socket
19
+
20
+ def initialize(&block)
21
+ @thread = Thread.new do
22
+ begin
23
+ @socket = nil
24
+ instance_eval(&block)
25
+ rescue Exception => x
26
+ puts "Exception in ThreadSocket: #{x}"
27
+ ensure
28
+ begin
29
+ @socket.shutdown(Socket::SHUT_RDWR) if @socket && @socket.is_a?(BasicSocket)
30
+ rescue
31
+ # ignore errors shutting down the socket
32
+ end
33
+ @socket = nil
34
+ end
35
+ end
36
+ end
37
+
38
+ # forward not implemented method calls to the thread
39
+ def method_missing(method_name, *args, &block)
40
+ if @thread && @thread.respond_to?(method_name)
41
+ @thread.__send__(method_name, *args, &block)
42
+ else
43
+ super(method_name, *args, &block)
44
+ end
45
+ end
46
+
47
+
48
+ end
@@ -0,0 +1,42 @@
1
+ require "rspec"
2
+ require 'spec_helper.rb'
3
+
4
+ TTCP_EXECUTABLE = File.expand_path('../../bin/ttcp', __FILE__)
5
+
6
+
7
+ def test_ttcp(options = '')
8
+ `DRY_RUN=YES #{TTCP_EXECUTABLE} #{options} 2>&1`
9
+ end
10
+
11
+ describe "Command Line Option Parsing" do
12
+
13
+ it "ttcp fails without -t or -r" do
14
+ test_ttcp
15
+ $?.should_not == 0
16
+ end
17
+
18
+ it "ttcp fails with both -t and -r" do
19
+ test_ttcp '-t -r'
20
+ $?.should_not == 0
21
+ end
22
+
23
+ it "ttcp fails without a host with -t" do
24
+ test_ttcp '-t'
25
+ $?.should_not == 0
26
+ end
27
+
28
+ it "ttcp is ok with a host and -t" do
29
+ test_ttcp '-t HOST'
30
+ $?.should == 0
31
+ end
32
+
33
+ it "ttcp is ok with -r and no host" do
34
+ test_ttcp '-r'
35
+ $?.should == 0
36
+ end
37
+
38
+ specify "ttcp prints out the version" do
39
+ version = test_ttcp '--version'
40
+ version.chomp.should == TTCP::VERSION
41
+ end
42
+ end
@@ -0,0 +1,219 @@
1
+ require "rspec"
2
+ require 'spec_helper'
3
+ require "stringio"
4
+
5
+
6
+ describe "TTCP Sockets" do
7
+
8
+ after() do
9
+ # ensure the ttcp instance is closed and destroyed
10
+ if @ttcp.is_a? TTCP::TTCP
11
+ @ttcp.close
12
+ end
13
+ @ttcp = nil
14
+ end
15
+
16
+ ##
17
+ ## Setting up ttcp socket
18
+ ##
19
+
20
+ specify "ttcp in transmit tcp makes a tcp socket" do
21
+ @ttcp = TTCP::TTCP.new :transmit => true, :tcp => true, :host => 'www.google.com', :port => 80
22
+ @ttcp.stdout_to_null
23
+ TTCP::TTCP.publicize_methods do
24
+ @ttcp.socket.should be_a(TCPSocket)
25
+ end
26
+ end
27
+
28
+ specify "ttcp in transmit udp makes a udp socket" do
29
+ @ttcp = TTCP::TTCP.new :transmit => true, :tcp => false, :udp => true, :host => 'localhost'
30
+ @ttcp.stdout_to_null
31
+ TTCP::TTCP.publicize_methods do
32
+ @ttcp.socket.should be_a(UDPSocket)
33
+ end
34
+ end
35
+
36
+ specify "ttcp in receive tcp makes a tcp socket" do
37
+ @ttcp = TTCP::TTCP.new :receive => true, :tcp => true, :host => 'localhost'
38
+ @ttcp.stdout_to_null
39
+ TTCP::TTCP.publicize_methods do
40
+ @ttcp.socket.should be_a(TCPSocket)
41
+ end
42
+ end
43
+
44
+ specify "ttcp in receive udp makes a udp socket" do
45
+ @ttcp = TTCP::TTCP.new :receive => true, :tcp => false, :udp => true, :host => 'localhost'
46
+ @ttcp.stdout_to_null
47
+ TTCP::TTCP.publicize_methods do
48
+ @ttcp.socket.should be_a(UDPSocket)
49
+ end
50
+ end
51
+
52
+ specify "ttcp in udp has a minimum buffer length greater than 4" do
53
+ @ttcp = TTCP::TTCP.new :transmit => true, :udp => true, :host => 'localhost', :length => 3
54
+ @ttcp.stdout_to_null
55
+ @ttcp.options[:length].should > 4
56
+ end
57
+
58
+ specify "ttcp without options raises exception because neither transmit nor receive is specified" do
59
+ lambda do
60
+ @ttcp = TTCP::TTCP.new
61
+ @ttcp.stdout_to_null
62
+ @ttcp.socket
63
+ end.should raise_error
64
+ end
65
+ end
66
+
67
+
68
+ describe "TTCP Transmitting" do
69
+
70
+ TEST_PORT = 5003
71
+
72
+ specify "TTCP transmit over TCP to no server fails" do
73
+ @ttcp = TTCP::TTCP.new :transmit => true, :tcp => true, :host => 'localhost', :port => TEST_PORT+1
74
+ @ttcp.stdout_to_null
75
+ lambda { @ttcp.run }.should raise_error
76
+ end
77
+
78
+ specify "TTCP transmits over TCP, closing the connection when it's done" do
79
+
80
+ thread = ThreadSocket.new do
81
+ TCPServer.open TEST_PORT do |server|
82
+ @socket = server.accept
83
+ until @socket.eof?
84
+ @socket.read 1000
85
+ end
86
+ end
87
+ end
88
+
89
+ sleep(0.2)
90
+
91
+ thread.alive?.should be_true
92
+ thread.socket.should be_nil
93
+
94
+ @ttcp = TTCP::TTCP.new :transmit => true, :tcp => true, :host => 'localhost', :port => TEST_PORT
95
+ @ttcp.stdout_to_null
96
+ lambda { @ttcp.run }.should_not raise_error
97
+ @ttcp.duration.should_not be_nil
98
+ @ttcp.duration.should > 0
99
+
100
+ thread.join
101
+
102
+ # connection has closed in server
103
+ thread.socket.should be_nil
104
+
105
+ # thread should have finished
106
+ thread.alive?.should be_false
107
+ end
108
+
109
+
110
+ specify "TTCP transmit over UDP" do
111
+
112
+ thread = ThreadSocket.new do
113
+ @socket = UDPSocket.new
114
+ @socket.bind('localhost', TEST_PORT)
115
+ sentinel_count = 0
116
+ until sentinel_count >= 2
117
+ b = @socket.recv 2000
118
+ sentinel_count += 1 if b.length <= 4
119
+ end
120
+ end
121
+
122
+ sleep(0.2)
123
+
124
+ thread.alive?.should be_true
125
+ thread.socket.should_not be_nil
126
+
127
+ @ttcp = TTCP::TTCP.new :transmit => true, :tcp => false, :udp => true, :host => 'localhost', :port => TEST_PORT
128
+ @ttcp.stdout_to_null
129
+ lambda { @ttcp.run }.should_not raise_error
130
+ @ttcp.duration.should_not be_nil
131
+ @ttcp.duration.should > 0
132
+
133
+ thread.join
134
+
135
+ # connection has closed in server
136
+ thread.socket.should be_nil
137
+
138
+ # thread should have finished
139
+ thread.alive?.should be_false
140
+ end
141
+
142
+ specify "TTCP receives over TCP from another TTCP sending to TCP" do
143
+
144
+ thread = Thread.new do
145
+
146
+ sleep 0.3
147
+
148
+ ttcp2 = TTCP::TTCP.new :transmit => true, :tcp => true, :host => 'localhost', :port =>TEST_PORT
149
+ ttcp2.stdout_to_null
150
+ ttcp2.run
151
+
152
+ end
153
+
154
+ sleep 0.1
155
+
156
+ thread.alive?.should be_true
157
+
158
+ @ttcp = TTCP::TTCP.new :receive=> true, :tcp => true, :host => 'localhost', :port =>TEST_PORT
159
+ @ttcp.stdout_to_null
160
+ @ttcp.run
161
+
162
+ @ttcp.duration.should_not be_nil
163
+ @ttcp.duration.should > 0
164
+
165
+ thread.join
166
+ thread.alive?.should be_false
167
+ end
168
+
169
+
170
+ specify "TTCP receives over UDP from another TTCP sending to UDP" do
171
+
172
+ thread = Thread.new do
173
+
174
+ sleep 0.3
175
+
176
+ ttcp2 = TTCP::TTCP.new :transmit => true, :tcp => false, :udp => true, :host => 'localhost', :port =>TEST_PORT
177
+ ttcp2.stdout_to_null
178
+ ttcp2.run
179
+
180
+ end
181
+
182
+ sleep 0.1
183
+
184
+ thread.alive?.should be_true
185
+
186
+ @ttcp = TTCP::TTCP.new :receive=> true, :tcp => false, :udp => true, :host => 'localhost', :port =>TEST_PORT
187
+ @ttcp.stdout_to_null
188
+ @ttcp.run
189
+
190
+ @ttcp.duration.should_not be_nil
191
+ @ttcp.duration.should > 0
192
+
193
+ thread.join
194
+ thread.alive?.should be_false
195
+ end
196
+
197
+
198
+ end
199
+
200
+ describe "Output formatting" do
201
+
202
+ [
203
+ [ 'Kilobytes', 'K', 1.5*1024, 2.0, "0.750 K" ],
204
+ [ 'Kilobits', 'k', 16*1024, 2.0, "1.000 Kbit" ],
205
+ [ 'Megabytes', 'M', 1.5*1024**2, 2.0, "0.750 M" ],
206
+ [ 'Gigabytes', 'G', 4.5*1024**3, 2.0, "2.250 G" ],
207
+ [ 'Megabits', 'm', 16*1024**2, 2.0, "1.000 Mbit" ],
208
+ [ 'Gigabits', 'g', 16*1024**3, 2.0, "1.000 Gbit" ]
209
+ ].each do |spec|
210
+
211
+ it "Calculates #{spec[0]} / sec correctly" do
212
+ @ttcp = TTCP::TTCP.new :format => spec[1]
213
+ result = @ttcp.instance_eval { format_rate(spec[2], spec[3]) }
214
+ result.should == spec[4]
215
+ end
216
+ end
217
+
218
+
219
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ttcp/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ttcp"
7
+ s.version = TTCP::VERSION
8
+ s.authors = ["Matt Connolly"]
9
+ s.email = ["matt@soundevolution.com.au"]
10
+ s.homepage = ""
11
+ s.summary = %q{A ruby implementation of the TTCP network test program.}
12
+ s.description = %q{Based on the C command line tool at http://www.pcausa.com/Utilities/pcattcp.htm}
13
+
14
+ s.rubyforge_project = "ttcp-rb"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "bundler"
23
+ s.add_development_dependency "rake"
24
+ s.add_development_dependency "rspec"
25
+ s.add_development_dependency "ci_reporter"
26
+ # s.add_runtime_dependency "rest-client"
27
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ttcp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Connolly
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: &70279976951600 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70279976951600
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70279976951160 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70279976951160
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70279976950640 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70279976950640
47
+ - !ruby/object:Gem::Dependency
48
+ name: ci_reporter
49
+ requirement: &70279984658600 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70279984658600
58
+ description: Based on the C command line tool at http://www.pcausa.com/Utilities/pcattcp.htm
59
+ email:
60
+ - matt@soundevolution.com.au
61
+ executables:
62
+ - ttcp
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - .jrubyrc
68
+ - Gemfile
69
+ - Guardfile
70
+ - LICENSE
71
+ - README.md
72
+ - Rakefile
73
+ - bin/ttcp
74
+ - lib/ttcp.rb
75
+ - lib/ttcp/version.rb
76
+ - spec/spec_helper.rb
77
+ - spec/ttcp_options_spec.rb
78
+ - spec/ttcp_spec.rb
79
+ - ttcp.gemspec
80
+ homepage: ''
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project: ttcp-rb
100
+ rubygems_version: 1.8.10
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: A ruby implementation of the TTCP network test program.
104
+ test_files:
105
+ - spec/spec_helper.rb
106
+ - spec/ttcp_options_spec.rb
107
+ - spec/ttcp_spec.rb
108
+ has_rdoc: