Author: aconway Date: Thu Mar 14 19:00:36 2013 New Revision: 1456613 URL: http://svn.apache.org/r1456613 Log: PROTON-215: Added ruby interop test.
Added a ruby version of the interop test. - Added convenience classes to Data to handle array, map, described and null. - Added a Type object to describe Data types. All tests (python, java, ruby) are passing. - Disabled failing test in message_spec.rb, see PROTON-272 Added: qpid/proton/trunk/tests/ruby/ qpid/proton/trunk/tests/ruby/proton-test (with props) qpid/proton/trunk/tests/ruby/proton_tests/ qpid/proton/trunk/tests/ruby/proton_tests/interop.rb (with props) Modified: qpid/proton/trunk/CMakeLists.txt qpid/proton/trunk/config.sh (contents, props changed) qpid/proton/trunk/proton-c/CMakeLists.txt qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/message.rb qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/data_spec.rb qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/message_spec.rb qpid/proton/trunk/tests/python/proton_tests/interop.py Modified: qpid/proton/trunk/CMakeLists.txt URL: http://svn.apache.org/viewvc/qpid/proton/trunk/CMakeLists.txt?rev=1456613&r1=1456612&r2=1456613&view=diff ============================================================================== --- qpid/proton/trunk/CMakeLists.txt (original) +++ qpid/proton/trunk/CMakeLists.txt Thu Mar 14 19:00:36 2013 @@ -101,9 +101,9 @@ install (FILES LICENSE README TODO # add relevant CTest support find_program (MAVEN_EXECUTABLE mvn DOC "Location of the maven program") -if (JAVA_FOUND AND MAVEN_EXECUTABLE AND DEFINED ENV{M2_HOME}) +if (JAVA_FOUND AND MAVEN_EXECUTABLE) add_test (proton-jni mvn test -P proton-jni --file ${CMAKE_CURRENT_SOURCE_DIR}/pom.xml) set_tests_properties(proton-jni PROPERTIES PASS_REGULAR_EXPRESSION "Tests run:.*Failures: 0, Errors: 0") -else (JAVA_FOUND AND MAVEN_EXECUTABLE AND DEFINED ENV{M2_HOME}) +else (JAVA_FOUND AND MAVEN_EXECUTABLE) message (STATUS "Cannot find both Java and Maven: testing disabled for Proton-J and JNI Bindings") -endif (JAVA_FOUND AND MAVEN_EXECUTABLE AND DEFINED ENV{M2_HOME}) +endif (JAVA_FOUND AND MAVEN_EXECUTABLE) Modified: qpid/proton/trunk/config.sh URL: http://svn.apache.org/viewvc/qpid/proton/trunk/config.sh?rev=1456613&r1=1456612&r2=1456613&view=diff ============================================================================== --- qpid/proton/trunk/config.sh (original) +++ qpid/proton/trunk/config.sh Thu Mar 14 19:00:36 2013 @@ -49,7 +49,7 @@ fi # Ruby export RUBY_BINDINGS=$PROTON_BINDINGS/ruby -export RUBYLIB=$RUBY_BINDINGS:$PROTON_HOME/proton-c-bindings/ruby/lib +export RUBYLIB=$RUBY_BINDINGS:$PROTON_HOME/proton-c/bindings/ruby/lib:$PROTON_HOME/tests/ruby # Perl export PERL_BINDINGS=$PROTON_BINDINGS/perl Propchange: qpid/proton/trunk/config.sh ------------------------------------------------------------------------------ svn:executable = * Modified: qpid/proton/trunk/proton-c/CMakeLists.txt URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/CMakeLists.txt?rev=1456613&r1=1456612&r2=1456613&view=diff ============================================================================== --- qpid/proton/trunk/proton-c/CMakeLists.txt (original) +++ qpid/proton/trunk/proton-c/CMakeLists.txt Thu Mar 14 19:00:36 2013 @@ -30,15 +30,15 @@ endif(WIN32 AND NOT CYGWIN) # Can't use ${CMAKE_VERSION) as it is not available in all versions of cmake 2.6 if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_LESS "2.8.0") - # OPTIONAL does not exist in install before 2.8 so always make docs and install - set (OPTIONAL_ARG "") - add_custom_target(docs ALL) - # There are bugs in the OpenSSL detection that mean -lcrypto is missed from the link line - # so turn off unknown symbol warnings - set (NOENABLE_UNDEFINED_ERROR ON) + # OPTIONAL does not exist in install before 2.8 so always make docs and install + set (OPTIONAL_ARG "") + add_custom_target(docs ALL) + # There are bugs in the OpenSSL detection that mean -lcrypto is missed from the link line + # so turn off unknown symbol warnings + set (NOENABLE_UNDEFINED_ERROR ON) else() - set (OPTIONAL_ARG OPTIONAL) - add_custom_target(docs) + set (OPTIONAL_ARG OPTIONAL) + add_custom_target(docs) endif() # Set the default SSL/TLS implementation @@ -46,14 +46,14 @@ find_package(OpenSSL) set(ssl_impl, none) if (OPENSSL_FOUND) - set(ssl_impl openssl) + set(ssl_impl openssl) endif(OPENSSL_FOUND) set(SSL_IMPL ${ssl_impl} CACHE STRING "Library to use for SSL/TLS support. Valid values: 'none','openssl'") configure_file ( "${CMAKE_CURRENT_SOURCE_DIR}/pn_config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/pn_config.h" -) + ) include_directories ("${CMAKE_CURRENT_BINARY_DIR}") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/include") @@ -62,13 +62,13 @@ add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/encodings.h COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/env.py PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR} python ${CMAKE_CURRENT_SOURCE_DIR}/src/codec/encodings.h.py > ${CMAKE_CURRENT_BINARY_DIR}/encodings.h DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/codec/encodings.h.py -) + ) add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/protocol.h COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/env.py PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR} python ${CMAKE_CURRENT_SOURCE_DIR}/src/protocol.h.py > ${CMAKE_CURRENT_BINARY_DIR}/protocol.h DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/protocol.h.py -) + ) # Select driver if(PN_WINAPI) @@ -128,20 +128,20 @@ else (UUID_GENERATE_IN_LIBC) endif (UUID_GENERATE_IN_LIBC) if (PN_WINAPI) -CHECK_SYMBOL_EXISTS(strerror_s "string.h" STRERROR_S_IN_WINAPI) -if (STRERROR_S_IN_WINAPI) - list(APPEND PLATFORM_DEFINITIONS "USE_STRERROR_S") -else (STRERROR_S_IN_WINAPI) -if (MINGW) - message (STATUS, "NOTE: your MinGW version lacks a thread safe strerror") - list(APPEND PLATFORM_DEFINITIONS "USE_OLD_STRERROR") -endif (MINGW) -endif (STRERROR_S_IN_WINAPI) + CHECK_SYMBOL_EXISTS(strerror_s "string.h" STRERROR_S_IN_WINAPI) + if (STRERROR_S_IN_WINAPI) + list(APPEND PLATFORM_DEFINITIONS "USE_STRERROR_S") + else (STRERROR_S_IN_WINAPI) + if (MINGW) + message (STATUS, "NOTE: your MinGW version lacks a thread safe strerror") + list(APPEND PLATFORM_DEFINITIONS "USE_OLD_STRERROR") + endif (MINGW) + endif (STRERROR_S_IN_WINAPI) else (PN_WINAPI) -CHECK_SYMBOL_EXISTS(strerror_r "string.h" STRERROR_R_IN_LIBC) -if (STRERROR_R_IN_LIBC) - list(APPEND PLATFORM_DEFINITIONS "USE_STRERROR_R") -endif (STRERROR_R_IN_LIBC) + CHECK_SYMBOL_EXISTS(strerror_r "string.h" STRERROR_R_IN_LIBC) + if (STRERROR_R_IN_LIBC) + list(APPEND PLATFORM_DEFINITIONS "USE_STRERROR_R") + endif (STRERROR_R_IN_LIBC) endif (PN_WINAPI) CHECK_SYMBOL_EXISTS(atoll "stdlib.h" C99_ATOLL) @@ -191,7 +191,7 @@ if (CMAKE_COMPILER_IS_GNUCC) set (COMPILE_PLATFORM_FLAGS "-std=gnu99") execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE) if (${GCC_VERSION} VERSION_LESS "4.3.0") # Only a concern if contibuting code back. message (STATUS "Old gcc version detected. C++ compatibility checks disabled") @@ -222,7 +222,7 @@ set (qpid-proton-platform ${pn_driver_impl} src/platform.c ${pn_driver_ssl_impl} -) + ) set (qpid-proton-core src/util.c @@ -245,27 +245,27 @@ set (qpid-proton-core ${CMAKE_CURRENT_BINARY_DIR}/encodings.h ${CMAKE_CURRENT_BINARY_DIR}/protocol.h -) + ) set_source_files_properties ( - ${qpid-proton-core} - PROPERTIES - COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_LANGUAGE_FLAGS}" -) + ${qpid-proton-core} + PROPERTIES + COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_LANGUAGE_FLAGS}" + ) set_source_files_properties ( - ${qpid-proton-platform} - PROPERTIES - COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}" - COMPILE_DEFINITIONS "${PLATFORM_DEFINITIONS}" -) + ${qpid-proton-platform} + PROPERTIES + COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}" + COMPILE_DEFINITIONS "${PLATFORM_DEFINITIONS}" + ) add_library ( qpid-proton SHARED ${qpid-proton-core} ${qpid-proton-platform} -) + ) target_link_libraries (qpid-proton ${UUID_LIB} ${SSL_LIB} ${TIME_LIB} ${PLATFORM_LIBS}) @@ -275,7 +275,7 @@ set_target_properties ( VERSION "${PN_LIB_SOMAJOR}.${PN_LIB_SOMINOR}" SOVERSION "${PN_LIB_SOMAJOR}" LINK_FLAGS "${CATCH_UNDEFINED}" -) + ) add_executable (proton src/proton.c) target_link_libraries (proton qpid-proton) @@ -284,10 +284,10 @@ add_executable (proton-dump src/proton-d target_link_libraries (proton-dump qpid-proton) set_target_properties ( - proton proton-dump - PROPERTIES - COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}" -) + proton proton-dump + PROPERTIES + COMPILE_FLAGS "${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS}" + ) if (BUILD_WITH_CXX) # tell CMake to use C++ for proton source files ending in ".c" @@ -301,8 +301,8 @@ endif (BUILD_WITH_CXX) # Install executables and libraries install (TARGETS proton proton-dump qpid-proton - RUNTIME DESTINATION bin - LIBRARY DESTINATION ${LIB_INSTALL_DIR}) + RUNTIME DESTINATION bin + LIBRARY DESTINATION ${LIB_INSTALL_DIR}) # Install header files file(GLOB headers "include/proton/*.[hi]") @@ -316,38 +316,74 @@ get_filename_component (LIBDIR ${CMAKE_I get_filename_component (INCLUDEDIR ${CMAKE_INSTALL_PREFIX}/${INCLUDE_INSTALL_DIR} ABSOLUTE) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/libqpid-proton.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/libqpid-proton.pc @ONLY) + ${CMAKE_CURRENT_BINARY_DIR}/libqpid-proton.pc @ONLY) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/libqpid-proton.pc - DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) + DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) # CTest -# suffix for Visual Studio targets. Only support RelWithDebInfo for now. + # suffix for Visual Studio targets. Only support RelWithDebInfo for now. if (MSVC) set (bld_suffix "/RelWithDebInfo") -endif (MSVC) - -# directories of interest -set (tdir1 "${pn_test_root}/python") -set (tdir2 "${CMAKE_CURRENT_SOURCE_DIR}/bindings/python") -set (tdir3 "${CMAKE_CURRENT_BINARY_DIR}/bindings/python") -set (tdir4 "${CMAKE_CURRENT_BINARY_DIR}${bld_suffix}") +endif(MSVC) +if (CMAKE_SYSTEM_NAME STREQUAL Windows) -# test #1: python [...]tests/python/proton-test + # Substitute windows separators in path and remove empty entries + function(set_path result path) + string (REGEX REPLACE "^:" "" path "${path}") + string (REGEX REPLACE "::" ":" path "${path}") + string (REGEX REPLACE "\\\\" "/" path "${path}") + string (REGEX REPLACE ":" ";" path "${path}") + set (${result} ${path} PARENT_SCOPE) + endfunction() -if (CMAKE_SYSTEM_NAME STREQUAL Windows) - # Windows uses a ';' separator and needs extra PATH help - set (newpath "PATH=$ENV{PATH};${tdir4}") - string (REGEX REPLACE "\\\\" "/" newpath "${newpath}") - add_test(proton-c python "${CMAKE_CURRENT_SOURCE_DIR}/env.py" - "PYTHONPATH=${tdir1};${tdir2};${tdir3};${tdir3}${bld_suffix}" - "${newpath}" - python "${pn_test_root}/python/proton-test") else (CMAKE_SYSTEM_NAME STREQUAL Windows) - add_test(proton-c python "${CMAKE_CURRENT_SOURCE_DIR}/env.py" - "PYTHONPATH=${tdir1}:${tdir2}:${tdir3}" - python "${pn_test_root}/python/proton-test") + + # Set variable result to path, with empty entries removed + function(set_path result path) + string (REGEX REPLACE "^:" "" path "${path}") + string (REGEX REPLACE "::" ":" path "${path}") + set (${result} ${path} PARENT_SCOPE) + endfunction() + endif (CMAKE_SYSTEM_NAME STREQUAL Windows) -set_tests_properties(proton-c PROPERTIES PASS_REGULAR_EXPRESSION "Totals:.*, 0 failed") + +set (env_py "${CMAKE_CURRENT_SOURCE_DIR}/env.py" ) + +# python test: tests/ruby/proton-test +set (py_root "${pn_test_root}/python") +set (py_src "${CMAKE_CURRENT_SOURCE_DIR}/bindings/python") +set (py_bin "${CMAKE_CURRENT_BINARY_DIR}/bindings/python") +set (py_bld "${CMAKE_CURRENT_BINARY_DIR}${bld_suffix}") # For windows +set_path (py_path "$ENV{PATH}:${py_bin}:${py_bld}") +set_path (py_pythonpath "$ENV{PYTHONPATH}:${py_root}:${py_src}:${py_bin}:${py_bld}") + +add_test (python-test python ${env_py} "PATH=${py_path}" "PYTHONPATH=${py_pythonpath}" + "${py_root}/proton-test") + +find_program(ruby_exe "ruby") +if (ruby_exe) + set (rb_root "${pn_test_root}/ruby") + set (rb_src "${CMAKE_CURRENT_SOURCE_DIR}/bindings/ruby") + set (rb_bin "${CMAKE_CURRENT_BINARY_DIR}/bindings/ruby") + set (rb_bld "${CMAKE_CURRENT_BINARY_DIR}${bld_suffix}") + set_path (rb "$ENV{PATH}:${rb_bin}:${rb_bld}") + set_path (rb "$ENV{RUBYLIB}:${rb_root}:${rb_src}:${rb_bin}:${rb_bld}") + + # ruby unit tests: tests/ruby/proton-test + add_test (ruby-unit-test python ${env_py} "PATH=${rb_path}" "RUBYLIB=${rb_rubylib}" + "${rb_root}/proton-test") + + # ruby spec tests + find_program(rspec_exe rspec) + if (rspec_exe) + add_test (ruby-spec-test python ${env_py} "PATH=${rb_path}" "RUBYLIB=${rb_rubylib}" + ${rspec_exe}) + else(rspec_exe) + message (STATUS "Cannot find rspec, skipping rspec tests") + endif(rspec_exe) +else (ruby_exe) + message (STATUS "Cannot find ruby, skipping ruby tests") +endif (ruby_exe) Modified: qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb?rev=1456613&r1=1456612&r2=1456613&view=diff ============================================================================== --- qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb (original) +++ qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/data.rb Thu Mar 14 19:00:36 2013 @@ -17,6 +17,8 @@ # under the License. # +require 'cproton' + module Qpid module Proton @@ -51,27 +53,27 @@ module Qpid # # The following types of scalar values are supported: # - # * *:NULL* - # * *:BOOL* - # * *:UBYTE* - # * *:USHORT* - # * *:SHORT* - # * *:UINT* - # * *:INT* - # * *:ULONG* - # * *:LONG* - # * *:FLOAT* - # * *:DOUBLE* - # * *:BINARY* - # * *:STRING* - # * *:SYMBOL* + # * *NULL* + # * *BOOL* + # * *UBYTE* + # * *USHORT* + # * *SHORT* + # * *UINT* + # * *INT* + # * *ULONG* + # * *LONG* + # * *FLOAT* + # * *DOUBLE* + # * *BINARY* + # * *STRING* + # * *SYMBOL* # # The following types of compound values are supported: # - # * *:DESCRIBED* - # * *:ARRAY* - # * *:LIST* - # * *:MAP* + # * *DESCRIBED* + # * *ARRAY* + # * *LIST* + # * *MAP* # class Data @@ -115,13 +117,12 @@ module Qpid Cproton.pn_data_rewind(@data) end - # Advances the current node to tits next sibling and returns its types. + # Advances the current node to its next sibling and returns its types. # # If there is no next sibling the current node remains unchanged # and nil is returned. def next - found = Cproton.pn_data_next(@data) - return found ? found : nil + return Cproton.pn_data_next(@data) ? type : nil end # Advances the current node to its previous sibling and returns its type. @@ -129,8 +130,7 @@ module Qpid # If there is no previous sibling then the current node remains unchanged # and nil is return. def prev - found = Cproton.pn_data_prev(@data) - return found ? found : nil + return Cproton.pn_data_prev(@data) ? type : nil end # Sets the parent node to the current node and clears the current node. @@ -146,21 +146,26 @@ module Qpid Cproton.pn_data_exit(@data) end - # Returns the type of the current node. - def node_type + # Returns the numeric type code of the current node. + def type_code dtype = Cproton.pn_data_type(@data) return (dtype == -1) ? nil : dtype end - # Returns a representation of the data encoded. in AMQP format. + # Return the Type object for the current node + def type + Type.by_code(type_code) + end + + # Returns a representation of the data encoded in AMQP format. def encode - size = 1024 + buffer = "\0"*1024 loop do - (cd, enc) = Cproton.pn_data_encode(@data, size) + cd = Cproton.pn_data_encode(@data, buffer, buffer.length) if cd == Cproton::PN_OVERFLOW - size *= 2 + buffer *= 2 elsif cd >= 0 - return enc + return buffer[0...cd] else check(cd) end @@ -175,7 +180,7 @@ module Qpid # * encoded - the encoded data # def decode(encoded) - check(Cproton.pn_data_decode(@data, encoded)) + check(Cproton.pn_data_decode(@data, encoded, encoded.length)) end # Puts a list value. @@ -208,7 +213,7 @@ module Qpid # @data.enter # (0...count).each # type = @data.next - # puts "Value: #{@data.string}" if type == :STRING + # puts "Value: #{@data.string}" if type == STRING # # ... process other node types # end def list @@ -244,10 +249,10 @@ module Qpid # @data.enter # (0...count).each do # type = @data.next - # puts "Key=#{@data.string}" if type == :STRING + # puts "Key=#{@data.string}" if type == STRING # # ... process other key types # type = @data.next - # puts "Value=#{@data.string}" if type == :STRING + # puts "Value=#{@data.string}" if type == STRING # # ... process other value types # end # @data.exit @@ -273,7 +278,7 @@ module Qpid # # # create an array of integer values # data = Qpid::Proton::Data.new - # data.put_array(false, :INT) + # data.put_array(false, INT) # data.enter # data.int = 1 # data.int = 2 @@ -281,7 +286,7 @@ module Qpid # data.exit # # # create an array of double values - # data.put_array(true, :DOUBLE) + # data.put_array(true, DOUBLE) # data.enter # data.symbol = "array-descriptor" # data.double = 1.1 @@ -290,8 +295,7 @@ module Qpid # data.exit # def put_array(described, element_type) - check(Cproton.pn_data_put_array(@data, described, - Data.type_value(element_type))) + check(Cproton.pn_data_put_array(@data, described, element_type.code)) end # If the current node is an array, returns a tuple of the element count, a @@ -319,12 +323,8 @@ module Qpid count = Cproton.pn_data_get_array(@data) described = Cproton.pn_data_is_array_described(@data) array_type = Cproton.pn_data_get_array_type(@data) - if array_type == -1 - array_type = nil - else - array_type = Data.type_name(array_type) - end - [count, described, array_type] + return nil if array_type == -1 + [count, described, Type.by_code(array_type) ] end # Puts a described value. @@ -719,38 +719,248 @@ module Qpid Cproton.pn_data_get_symbol(@data) end - def self.add_item(key, value, name) # :nodoc: - @@type_value ||= {} - @@type_value[key] = value + # Convenience class for described types. + # + # Holds the descriptor and value, implements methods to get and put a + # described type in a Data object. + Described = Struct.new(:descriptor, :value) + class Described + def self.get(data) + def fail; raise 'Not a described type'; end + (data.described? and data.enter) or fail + begin + data.next or fail + descriptor = data.get + data.next or fail + value = data.get + return Described.new(descriptor, value) + ensure + data.exit + end + end - @@type_name ||= {} - @@type_name[value] = key + def put(data) + described + enter + data.put(@descriptor) + data.put(@value) + end end - # Returns the typename for the specified type. + # Convenience class for arrays # - # ==== Examples - # # returns "null" - # name = Qpid::Proton::Data.type_name(:NULL) + # Convenience class for arrays. # - def self.type_name(key) - @@type_name[key] + # An Array with methods to get and put the array in a Data object + # as an AMQP array. + class Array < ::Array + def initialize(descriptor, type, elements=[]) + @descriptor, @type = descriptor, type + super(elements.collect { |x| x }) + end + + attr_reader :descriptor, :type + + def ==(o) super; end + def eql?(o) + o.class == self.class && @descriptor == o.descriptor && @type == o.type && + super + end + + + def self.get(data) + count, described, type = data.array + data.enter or raise 'Not an array' + begin + descriptor = nil + if described then + data.next; descriptor = data.get + end + elements = [] + while data.next do; elements << data.get; end + elements.size == count or + raise "Array wrong length, expected #{count} but got #{elements.size}" + return Array.new(descriptor, type, elements) + ensure + data.exit + end + end + + def put(data) + data.put_array(@descriptor, @type) + data.enter + begin + data.put(@descriptor) if @descriptor + elements.each { |e| data.put(e); } + ensure + data.exit + end + end end - def self.type_value(key) # :nodoc: - @@type_value[key] + # Convenience class for arrays. + # + # An array with methods to get and put the in a Data object + # as an AMQP list. + class List < ::Array + + def initialize(elements) super; end + + def self.get(data) + def fail; raise 'Not a list'; end + size = data.list + data.enter or fail + begin + elements = [] + while data.next do elements << data.get; end + elements.size == size or + raise "List wrong length, expected #{size} but got #{elements.size}" + return List.new(elements) + ensure + data.exit + end + end + + def put(data) + data.put_list + @elements.each { |e| data.put(e); } + end end - def self.const_missing(key) # :nodoc: - @@type_value[key] + # Convenience class for maps + # + # A Hash with methods to get and put the map in a Data object as an AMQP + # map. + class Map < ::Hash + def initialize(map) super; end + + def self.get(data) + data.enter or raise "Not a Map" + begin + map = {} + while data.next + k = data.get + data.next or raise "Map missing final value" + map[k] = data.get + end + return map + ensure + data.exit + end + end + + def put(data) + data.enter + begin + @map.each { |k,v| data.put(k); data.put(v); } + ensure + data.exit + end + end + end + + # Convenience class for null values + # + # Empty object with methods to get and put it in a Data object. + class Null + def initialize; end + + def self.get(data) + data.null? or raise "Not a null" + return nil + end + + def put(data); data.null; end + end + + # Information about AMQP types including how to get/put an object of that + # type in a Data object. + # + # A convenience class is provided for each of the compound types + # to allow get/put of those types as a single object. + class Type + attr_reader :code, :name + + def initialize(code,name,klass=nil) + @code, @name, @klass = code,name,klass; + @get,@put = name.intern,(name+"=").intern if !klass + @@by_code ||= {} + @@by_code[code] = self + @@by_name ||= {} + @@by_name[name] = self + end + + def get(data) + if @klass then @klass.send(:get, data) + else data.send(@get); end + end + + def put(data, value) + if @klass then @klass.send(:put, data, value) + else data.send(@put, value); end + end + + def to_s() return name; end + def self.by_name(name) @@by_name[name]; end + def self.by_code(code) @@by_code[code]; end + + + def self.get(data) + if @klass then @klass.send(:get, data) + else data.send(@get); end + end + + def put(data, value) + if @klass then @klass.send(:put, data, value) + else data.send(@put, value); end + + def to_s name; end + end end + # Get the current value as a single object. + def get + type.get(self); + end + + # Put value as an object of type type_ + def put(value, type_); + type_.put(self, value); + end + + # Constants for all the supported types + NULL = Type.new(Cproton::PN_NULL, "null", Null) + BOOL = Type.new(Cproton::PN_BOOL, "bool") + UBYTE = Type.new(Cproton::PN_UBYTE, "ubyte") + BYTE = Type.new(Cproton::PN_BYTE, "byte") + USHORT = Type.new(Cproton::PN_USHORT, "ushort") + SHORT = Type.new(Cproton::PN_SHORT, "short") + UINT = Type.new(Cproton::PN_UINT, "uint") + INT = Type.new(Cproton::PN_INT, "int") + CHAR = Type.new(Cproton::PN_CHAR, "char") + ULONG = Type.new(Cproton::PN_ULONG, "ulong") + LONG = Type.new(Cproton::PN_LONG, "long") + TIMESTAMP = Type.new(Cproton::PN_TIMESTAMP, "timestamp") + FLOAT = Type.new(Cproton::PN_FLOAT, "float") + DOUBLE = Type.new(Cproton::PN_DOUBLE, "double") + DECIMAL32 = Type.new(Cproton::PN_DECIMAL32, "decimal32") + DECIMAL64 = Type.new(Cproton::PN_DECIMAL64, "decimal64") + DECIMAL128 = Type.new(Cproton::PN_DECIMAL128, "decimal128") + UUID = Type.new(Cproton::PN_UUID, "uuid") + BINARY = Type.new(Cproton::PN_BINARY, "binary") + STRING = Type.new(Cproton::PN_STRING, "string") + SYMBOL = Type.new(Cproton::PN_SYMBOL, "symbol") + DESCRIBED = Type.new(Cproton::PN_DESCRIBED, "described", Described) + ARRAY = Type.new(Cproton::PN_ARRAY, "array", Array) + LIST = Type.new(Cproton::PN_LIST, "list", List) + MAP = Type.new(Cproton::PN_MAP, "map", Map) + private def valid_uuid?(value) - # ensure that the UUID is in the right format - # xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx - value =~ /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ + # ensure that the UUID is in the right format + # xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx + value =~ /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ end def check(err) # :nodoc: @@ -760,35 +970,6 @@ module Qpid return err end end - - self.add_item(:NULL, Cproton::PN_NULL, "null") - self.add_item(:BOOL, Cproton::PN_BOOL, "bool") - self.add_item(:UBYTE, Cproton::PN_UBYTE, "ubyte") - self.add_item(:BYTE, Cproton::PN_BYTE, "byte") - self.add_item(:USHORT, Cproton::PN_USHORT, "ushort") - self.add_item(:SHORT, Cproton::PN_SHORT, "short") - self.add_item(:UINT, Cproton::PN_UINT, "uint") - self.add_item(:INT, Cproton::PN_INT, "int") - self.add_item(:CHAR, Cproton::PN_CHAR, "char") - self.add_item(:ULONG, Cproton::PN_ULONG, "ulong") - self.add_item(:LONG, Cproton::PN_LONG, "long") - self.add_item(:TIMESTAMP, Cproton::PN_TIMESTAMP, "timestamp") - self.add_item(:FLOAT, Cproton::PN_FLOAT, "float") - self.add_item(:DOUBLE, Cproton::PN_DOUBLE, "double") - self.add_item(:DECIMAL32, Cproton::PN_DECIMAL32, "decimal32") - self.add_item(:DECIMAL64, Cproton::PN_DECIMAL64, "decimal64") - self.add_item(:DECIMAL128, Cproton::PN_DECIMAL128, "decimal128") - self.add_item(:UUID, Cproton::PN_UUID, "uuid") - self.add_item(:BINARY, Cproton::PN_BINARY, "binary") - self.add_item(:STRING, Cproton::PN_STRING, "string") - self.add_item(:SYMBOL, Cproton::PN_SYMBOL, "symbol") - self.add_item(:DESCRIBED, Cproton::PN_DESCRIBED, "described") - self.add_item(:ARRAY, Cproton::PN_ARRAY, "array") - self.add_item(:LIST, Cproton::PN_LIST, "list") - self.add_item(:MAP, Cproton::PN_MAP, "map") - end - end - end Modified: qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/message.rb URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/message.rb?rev=1456613&r1=1456612&r2=1456613&view=diff ============================================================================== --- qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/message.rb (original) +++ qpid/proton/trunk/proton-c/bindings/ruby/lib/qpid_proton/message.rb Thu Mar 14 19:00:36 2013 @@ -27,6 +27,17 @@ module Qpid # class Message + # Decodes a message from supplied AMQP data and returns the number + # of bytes consumed. + # + # ==== Options + # + # * encoded - the encoded data + # + def decode(encoded) + check(Cproton.pn_message_decode(@impl, encoded, encoded.length)) + end + # Creates a new +Message+ instance. def initialize @impl = Cproton.pn_message @@ -427,6 +438,15 @@ module Qpid Cproton.pn_message_get_reply_to_group_id(@impl) end + private + + def check(err) # :nodoc: + if err < 0 + raise DataError, "[#{err}]: #{Cproton.pn_message_error(@data)}" + else + return err + end + end end end Modified: qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/data_spec.rb URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/data_spec.rb?rev=1456613&r1=1456612&r2=1456613&view=diff ============================================================================== --- qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/data_spec.rb (original) +++ qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/data_spec.rb Thu Mar 14 19:00:36 2013 @@ -414,7 +414,7 @@ module Qpid it "can hold an array" do values = [] (1..(rand(100) + 5)).each { values << rand(2**16) } - @data.put_array false, :INT + @data.put_array false, Data::INT @data.enter values.each { |value| @data.int = value } @data.exit @@ -430,13 +430,13 @@ module Qpid values = [] (1..(rand(100) + 5)).each { values << random_string(64) } descriptor = random_string(32) - @data.put_array true, :STRING + @data.put_array true, Data::STRING @data.enter @data.symbol = descriptor values.each { |value| @data.string = value } @data.exit - @data.array.should == [values.size, true, :STRING] + @data.array.should == [values.size, true, Data::STRING] @data.enter @data.next @data.symbol.should == descriptor Modified: qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/message_spec.rb URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/message_spec.rb?rev=1456613&r1=1456612&r2=1456613&view=diff ============================================================================== --- qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/message_spec.rb (original) +++ qpid/proton/trunk/proton-c/bindings/ruby/spec/qpid/proton/message_spec.rb Thu Mar 14 19:00:36 2013 @@ -401,7 +401,7 @@ module Qpid @message.reply_to_group_id.should eq(id) end - it "can have nil content" do + xit "can have nil content" do @message.content = nil @message.content.should be_nil end Modified: qpid/proton/trunk/tests/python/proton_tests/interop.py URL: http://svn.apache.org/viewvc/qpid/proton/trunk/tests/python/proton_tests/interop.py?rev=1456613&r1=1456612&r2=1456613&view=diff ============================================================================== --- qpid/proton/trunk/tests/python/proton_tests/interop.py (original) +++ qpid/proton/trunk/tests/python/proton_tests/interop.py Thu Mar 14 19:00:36 2013 @@ -43,7 +43,7 @@ class InteropTest(common.Test): def get_data(self, name): filename = os.path.join(test_interop_dir, name+".amqp") - f = open(filename) + f = open(filename,"rb") try: return f.read() finally: f.close() @@ -105,10 +105,7 @@ class InteropTest(common.Test): def test_described(self): self.decode_data_file("described") - assert self.data.next() == Data.DESCRIBED - self.data.enter() - self.assert_next(Data.SYMBOL, "foo-descriptor") - self.assert_next(Data.STRING, "foo-value") + self.assert_next(Data.DESCRIBED, Described("foo-descriptor", "foo-value")) self.data.exit() assert self.data.next() == Data.DESCRIBED Added: qpid/proton/trunk/tests/ruby/proton-test URL: http://svn.apache.org/viewvc/qpid/proton/trunk/tests/ruby/proton-test?rev=1456613&view=auto ============================================================================== --- qpid/proton/trunk/tests/ruby/proton-test (added) +++ qpid/proton/trunk/tests/ruby/proton-test Thu Mar 14 19:00:36 2013 @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +require 'test/unit' +require 'proton_tests/interop.rb' Propchange: qpid/proton/trunk/tests/ruby/proton-test ------------------------------------------------------------------------------ svn:executable = * Added: qpid/proton/trunk/tests/ruby/proton_tests/interop.rb URL: http://svn.apache.org/viewvc/qpid/proton/trunk/tests/ruby/proton_tests/interop.rb?rev=1456613&view=auto ============================================================================== --- qpid/proton/trunk/tests/ruby/proton_tests/interop.rb (added) +++ qpid/proton/trunk/tests/ruby/proton_tests/interop.rb Thu Mar 14 19:00:36 2013 @@ -0,0 +1,122 @@ +#!/usr/bin/env ruby + +require 'test/unit' +require 'qpid_proton' + +class InteropTest < Test::Unit::TestCase + Data = Qpid::Proton::Data + Type = Data::Type + Message = Qpid::Proton::Message + + def setup + @data = Data.new + @message = Message.new + end + + # Walk up the directory tree to find the tests directory. + def get_data(name) + path = File.absolute_path(__FILE__) + while path and File.basename(path) != "tests" do path = File.dirname(path); end + path = File.join(path,"interop") + raise "Can't find test/interop directory from #{__FILE__}" unless File.directory?(path) + path = File.join(path,"#{name}.amqp") + File.open(path, "rb") { |f| f.read } + end + + # Decode encoded bytes as a Data object + def decode_data(encoded) + buffer = encoded + while buffer.size > 0 + n = @data.decode(buffer) + buffer = buffer[n..-1] + end + @data.rewind + reencoded = @data.encode + # Test the round-trip re-encoding gives the same result. + assert_equal(encoded, reencoded) + end + + def decode_data_file(name) decode_data(get_data(name)); end + + def decode_message_file(name) + message = Message.new() + message.decode(self.get_data(name)) + self.decode_data(message.content) + end + + def assert_next(type, value) + assert @data.next + assert_equal(type, @data.type) + assert_equal(value, type.get(@data)) + end + + def test_message + decode_message_file("message") + assert_next(Data::STRING, "hello") + assert !@data.next() + end + + def test_primitives + decode_data_file("primitives") + assert_next(Data::BOOL, true) + assert_next(Data::BOOL, false) + assert_next(Data::UBYTE, 42) + assert_next(Data::USHORT, 42) + assert_next(Data::SHORT, -42) + assert_next(Data::UINT, 12345) + assert_next(Data::INT, -12345) + assert_next(Data::ULONG, 12345) + assert_next(Data::LONG, -12345) + assert_next(Data::FLOAT, 0.125) + assert_next(Data::DOUBLE, 0.125) + assert !@data.next() + end + + def test_strings + decode_data_file("strings") + assert_next(Data::BINARY, "abc\0defg") + assert_next(Data::STRING, "abcdefg") + assert_next(Data::SYMBOL, "abcdefg") + assert_next(Data::BINARY, "") + assert_next(Data::STRING, "") + assert_next(Data::SYMBOL, "") + assert !@data.next() + end + + def test_described + decode_data_file("described") + assert_next(Data::DESCRIBED, Data::Described.new("foo-descriptor", "foo-value")) + assert(@data.described?) + assert_next(Data::DESCRIBED, Data::Described.new(12, 13)) + assert(@data.described?) + assert !@data.next + end + + def test_described_array + decode_data_file("described_array") + assert_next(Data::ARRAY, Data::Array.new("int-array", Data::INT, 0...10)) + end + + def test_arrays + decode_data_file("arrays") + assert_next(Data::ARRAY, Data::Array.new(false, Data::INT, 0...100)) + assert_next(Data::ARRAY, Data::Array.new(false, Data::STRING, ["a", "b", "c"])) + assert_next(Data::ARRAY, Data::Array.new(false, Data::INT)) + assert !@data.next + end + + def test_lists + decode_data_file("lists") + assert_next(Data::LIST, [32, "foo", true]) + assert_next(Data::LIST, []) + assert !@data.next + end + + def test_maps + decode_data_file("maps") + assert_next(Data::MAP, {"one" => 1, "two" => 2, "three" => 3 }) + assert_next(Data::MAP, {1 => "one", 2 => "two", 3 => "three"}) + assert_next(Data::MAP, {}) + assert !@data.next() + end +end Propchange: qpid/proton/trunk/tests/ruby/proton_tests/interop.rb ------------------------------------------------------------------------------ svn:eol-style = native Propchange: qpid/proton/trunk/tests/ruby/proton_tests/interop.rb ------------------------------------------------------------------------------ svn:executable = * --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org