#!/usr/bin/ruby # helper function for netsed Unit::Tests # (c) 2010 Julien Viard de Galbert # # --------------------------------------------------------------------------- # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # # You may also redistribute it or any part of it under the Ruby license to # better integrate to your ruby scripts. # The ruby license is available at http://www.ruby-lang.org/en/LICENSE.txt # --------------------------------------------------------------------------- require 'socket' LH_IPv4 = '127.0.0.1' LH_IPv6 = '::1' #default values for all test classes LPORT=20000 RPORT=20001 SERVER=LH_IPv4 # Run a netsed instance with given parameters. class NetsedRun attr_reader :data # Launch netsed with given parameters. def initialize(proto, lport, rhost, rport, rules, options='') @cmd="../netsed #{options} #{proto} #{lport} #{rhost} #{rport} #{rules.join(' ')}" @pipe=IO.popen(@cmd) @data='' @pipe.sync = true # waiting for netsed to listen begin line = @pipe.gets @data << line.to_s end until line =~ /^\[\+\] Listening on port/ end # Kill (INT) and wait netsed exit # also returns standard output def kill Process.kill('INT', @pipe.pid) Process.wait(@pipe.pid) @data << @pipe.read @pipe.close return @data end # Returns netsed PID def pid @pipe.pid end end # TCP Server that accept multiple connections class TCPServeMultipleConnection # Creates a thread server on _addr_, _port_ # the block is called for every accepted connections (up to _nbconnections_) # once the block exits, the socket is closed. def initialize(addr, port, nbconnections) # :yields: socket, index dts = TCPServer.new(addr, port) @th = Thread.start { ths=[] for i in 0..nbconnections-1 do sa=dts.accept ths[i] = Thread.start(i, sa) {|j, s| yield s,j s.close } end ths.each {|tha| tha.join} dts.close } end # Wait for the server to complete, it will once all connections are processed. def join @th.join end end # TCP Server that accept a single connection class TCPServeSingleConnection # Creates a thread server on _addr_, _port_ # the block is called for first accepted connection # once the block exits, the socket is closed. def initialize(addr, port) # :yields: socket dts = TCPServer.new(addr, port) @th = Thread.start { s=dts.accept yield s s.close dts.close } end # Wait for the server to complete, it will once the connection is processed. def join @th.join end end # TCP Server that accept a single connection and sent data to it class TCPServeSingleDataSender < TCPServeSingleConnection # Creates a thread server on _addr_, _port_ that send _data_ on connection, # then closes the socket. def initialize(addr, port, data) super(addr, port) { |s| s.write(data) } end end # TCP Server that accept a single connection and receive data from it class TCPServeSingleDataReciever < TCPServeSingleConnection # Creates a thread server on _addr_, _port_ that receive up to _maxlen_ # on connection, then closes the socket. def initialize(addr, port, maxlen) super(addr, port) { |s| @datarecv=s.recv(maxlen) } end # Wait for the server to complete, and return the received data. def join super return @datarecv end end # Receive up to _maxlen_ data from a TCP Socket on _addr_,_port_ def TCPSingleDataRecv(addr, port, maxlen) streamSock = TCPSocket.new(addr, port) data = streamSock.recv( maxlen ) streamSock.close return data end # Send _data_ to a TCP Socket on _addr_,_port_ def TCPSingleDataSend(addr, port, data) streamSock = TCPSocket.new(addr, port) streamSock.write( data ) streamSock.close end # Send _data_ to a UDP Socket on _addr_,_port_ def UDPSingleDataSend(addr, port, data) dataSock = UDPSocket.new dataSock.connect(addr, port) dataSock.write( data ) dataSock.close end # Recursively compare two objects. Asserts if a difference was found. # # Note: The function is mostly inspired from the code snippet published # by Scott Bronson on http://gist.github.com/287675 # rewritten using Test::Unit::Assertions to better fit in tests def assert_equal_objects(expected, actual, message=nil, path='') if path=='' then prefix="Objects differ:" else prefix="Objects differ at #{path}:" end if expected.kind_of?(Hash) extraexpectedkeys = expected.keys - actual.keys full_message = build_message(message, "#{prefix} extra keys in expected: .\n", extraexpectedkeys) assert_block(full_message) { extraexpectedkeys.length == 0 } extraactualkeys = actual.keys - expected.keys full_message = build_message(message, "#{prefix} extra keys in actual: .\n", extraactualkeys) assert_block(full_message) { extraactualkeys.length == 0 } expectedkeys=expected.keys begin expectedkeys.sort! rescue NoMethodError end expectedkeys.each do |key| assert_equal_objects expected[key], actual[key], message, path.dup << build_message(nil,"[?]",key).to_s end elsif (expected.kind_of?(Enumerable) && !expected.kind_of?(String)) && (actual.kind_of?(Enumerable) && !actual.kind_of?(String)) full_message = build_message(message, "#{prefix} expected has #{expected.length} and actual has #{actual.length} items.\n expected but was\n.\n", expected, actual) assert_block(full_message) { expected.length == actual.length } (0..expected.length).each do |i| assert_equal_objects expected[i], actual[i], message, path.dup << "[#{i}]" end elsif expected != actual full_message = build_message(message, "#{prefix}\n expected but was\n.\n", expected, actual) assert_block(full_message) { false } end end # vim:sw=2:sta:et: