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

Reply via email to