Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package rubygem-ruby-dbus for 
openSUSE:Factory checked in at 2022-04-30 00:44:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-ruby-dbus (Old)
 and      /work/SRC/openSUSE:Factory/.rubygem-ruby-dbus.new.1538 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rubygem-ruby-dbus"

Sat Apr 30 00:44:35 2022 rev:27 rq:973057 version:0.18.0.beta5

Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-ruby-dbus/rubygem-ruby-dbus.changes      
2022-04-23 00:25:26.327755453 +0200
+++ 
/work/SRC/openSUSE:Factory/.rubygem-ruby-dbus.new.1538/rubygem-ruby-dbus.changes
    2022-04-30 00:44:55.414919439 +0200
@@ -1,0 +2,18 @@
+Wed Apr 27 08:29:35 UTC 2022 - Martin Vidner <mvid...@suse.com>
+
+- 0.18.0.beta5
+ API
+ * DBus::Type instances are frozen.
+ * Data::Container classes (Array, Struct, DictEntry, but not Variant)
+   constructors (#initialize, .from_items, .from_typed) changed to have
+   a *type* argument instead of *member_type* or *member_types*.
+ * Added type factories
+   * Type::Array[type]
+   * Type::Hash[key_type, value_type]
+   * Type::Struct[type1, type2...]
+
+ Bug fixes:
+ * Properties containing Variants would return them doubly wrapped
+   (gh#mvidner/ruby-dbus#111).
+
+-------------------------------------------------------------------

Old:
----
  ruby-dbus-0.18.0.beta4.gem

New:
----
  ruby-dbus-0.18.0.beta5.gem

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rubygem-ruby-dbus.spec ++++++
--- /var/tmp/diff_new_pack.1MXlrp/_old  2022-04-30 00:44:56.002919949 +0200
+++ /var/tmp/diff_new_pack.1MXlrp/_new  2022-04-30 00:44:56.010919956 +0200
@@ -24,7 +24,7 @@
 #
 
 Name:           rubygem-ruby-dbus
-Version:        0.18.0.beta4
+Version:        0.18.0.beta5
 Release:        0
 %define mod_name ruby-dbus
 %define mod_full_name %{mod_name}-%{version}

++++++ ruby-dbus-0.18.0.beta4.gem -> ruby-dbus-0.18.0.beta5.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/NEWS.md new/NEWS.md
--- old/NEWS.md 2022-04-21 13:44:41.000000000 +0200
+++ new/NEWS.md 2022-04-27 10:47:47.000000000 +0200
@@ -2,6 +2,23 @@
 
 ## Unreleased
 
+## Ruby D-Bus 0.18.0.beta5 - 2022-04-27
+
+API:
+ * DBus::Type instances are frozen.
+ * Data::Container classes (Array, Struct, DictEntry, but not Variant)
+   constructors (#initialize, .from_items, .from_typed) changed to have
+   a *type* argument instead of *member_type* or *member_types*.
+ * Added type factories
+   * Type::Array[type]
+   * Type::Hash[key_type, value_type]
+   * Type::Struct[type1, type2...]
+
+Bug fixes:
+ * Properties containing Variants would return them doubly wrapped ([#111][]).
+
+[#111]: https://github.com/mvidner/ruby-dbus/pull/111
+
 ## Ruby D-Bus 0.18.0.beta4 - 2022-04-21
 
 Bug fixes:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/VERSION new/VERSION
--- old/VERSION 2022-04-21 13:44:41.000000000 +0200
+++ new/VERSION 2022-04-27 10:47:47.000000000 +0200
@@ -1 +1 @@
-0.18.0.beta4
+0.18.0.beta5
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/dbus/data.rb new/lib/dbus/data.rb
--- old/lib/dbus/data.rb        2022-04-21 13:44:41.000000000 +0200
+++ new/lib/dbus/data.rb        2022-04-27 10:47:47.000000000 +0200
@@ -43,7 +43,7 @@
       data_class = Data::BY_TYPE_CODE[type.sigtype]
       # not nil because DBus.type validates
 
-      data_class.from_typed(value, member_types: type.members)
+      data_class.from_typed(value, type: type)
     end
     module_function :make_typed
 
@@ -67,6 +67,15 @@
       # for the specific see {Variant#member_type}
       # @return [Type] the exact type of this value
 
+      # @!method self.from_typed(value, type:)
+      # @param value [::Object]
+      # @param type [Type]
+      # @return [Base]
+      # @api private
+      # Use {Data.make_typed} instead.
+      # Construct an instance of the specific subclass, with a type further
+      # specified in the *type* argument.
+
       # Child classes must validate *value*.
       def initialize(value)
         @value = value
@@ -83,6 +92,11 @@
       # Hash key equality
       # See https://ruby-doc.org/core-3.0.0/Object.html#method-i-eql-3F
       alias eql? ==
+
+      # @param type [Type]
+      def self.assert_type_matches_class(type)
+        raise ArgumentError unless type.sigtype == type_code
+      end
     end
 
     # A value that is not a {Container}.
@@ -103,10 +117,10 @@
       end
 
       # @param value [::Object]
-      # @param member_types [::Array<Type>] (ignored, will be empty)
+      # @param type [Type]
       # @return [Basic]
-      def self.from_typed(value, member_types:) # rubocop:disable 
Lint/UnusedMethodArgument
-        # assert member_types.empty?
+      def self.from_typed(value, type:)
+        assert_type_matches_class(type)
         new(value)
       end
     end
@@ -132,34 +146,6 @@
       end
     end
 
-    # {DBus::Data::String}, {DBus::Data::ObjectPath}, or 
{DBus::Data::Signature}.
-    class StringLike < Basic
-      def self.fixed?
-        false
-      end
-
-      def initialize(value)
-        if value.is_a?(self.class)
-          value = value.value
-        else
-          self.class.validate_raw!(value)
-        end
-
-        super(value)
-      end
-    end
-
-    # Contains one or more other values.
-    class Container < Base
-      def self.basic?
-        false
-      end
-
-      def self.fixed?
-        false
-      end
-    end
-
     # Format strings for String#unpack, both little- and big-endian.
     Format = ::Struct.new(:little, :big)
 
@@ -398,6 +384,23 @@
       end
     end
 
+    # {DBus::Data::String}, {DBus::Data::ObjectPath}, or 
{DBus::Data::Signature}.
+    class StringLike < Basic
+      def self.fixed?
+        false
+      end
+
+      def initialize(value)
+        if value.is_a?(self.class)
+          value = value.value
+        else
+          self.class.validate_raw!(value)
+        end
+
+        super(value)
+      end
+    end
+
     # UTF-8 encoded string.
     class String < StringLike
       def self.type_code
@@ -494,6 +497,21 @@
       end
     end
 
+    # Contains one or more other values.
+    class Container < Base
+      def self.basic?
+        false
+      end
+
+      def self.fixed?
+        false
+      end
+
+      # For containers, the type varies among instances
+      # @see Base#type
+      attr_reader :type
+    end
+
     # An Array, or a Dictionary (Hash).
     class Array < Container
       def self.type_code
@@ -504,44 +522,32 @@
         4
       end
 
-      # @return [Type]
-      attr_reader :member_type
-
-      def type
-        return @type if @type
-
-        # TODO: reconstructing the type is cumbersome; have #initialize take 
*type* instead?
-        # TODO: or rather add Type::Array[t]
-        @type = Type.new("a")
-        @type << member_type
-        @type
-      end
-
       # TODO: check that Hash keys are basic types
       # @param mode [:plain,:exact]
-      # @param member_type [Type]
+      # @param type [Type]
       # @param hash [Boolean] are we unmarshalling an ARRAY of DICT_ENTRY
       # @return [Data::Array]
-      def self.from_items(value, mode:, member_type:, hash: false)
+      def self.from_items(value, mode:, type:, hash: false)
         value = Hash[value] if hash
         return value if mode == :plain
 
-        new(value, member_type: member_type)
+        new(value, type: type)
       end
 
       # @param value [::Object]
-      # @param member_types [::Array<Type>]
+      # @param type [Type]
       # @return [Data::Array]
-      def self.from_typed(value, member_types:)
+      def self.from_typed(value, type:)
+        assert_type_matches_class(type)
         # TODO: validation
-        member_type = member_types.first
+        member_type = type.child
 
         # TODO: Dict??
         items = value.map do |i|
           Data.make_typed(member_type, i)
         end
 
-        new(items, member_type: member_type) # initialize(::Array<Data::Base>)
+        new(items, type: type) # initialize(::Array<Data::Base>)
       end
 
       # FIXME: should Data::Array be mutable?
@@ -550,12 +556,12 @@
       # TODO: specify type or guess type?
       # Data is the exact type, so its constructor should be exact
       # and guesswork should be clearly labeled
-      # @param member_type [SingleCompleteType,Type]
-      def initialize(value, member_type:)
-        member_type = DBus.type(member_type) unless member_type.is_a?(Type)
+      # @param type [SingleCompleteType,Type]
+      def initialize(value, type:)
+        type = DBus.type(type) unless type.is_a?(Type)
+        self.class.assert_type_matches_class(type)
+        @type = type
         # TODO: copy from another Data::Array
-        @member_type = member_type
-        @type = nil
         super(value)
       end
     end
@@ -572,46 +578,32 @@
         8
       end
 
-      # @return [::Array<Type>]
-      attr_reader :member_types
-
-      def type
-        return @type if @type
-
-        # TODO: reconstructing the type is cumbersome; have #initialize take 
*type* instead?
-        # TODO: or rather add Type::Struct[t1, t2, ...]
-        @type = Type.new(self.class.type_code, abstract: true)
-        @member_types.each do |member_type|
-          @type << member_type
-        end
-        @type
-      end
-
       # @param value [::Array]
-      def self.from_items(value, mode:, member_types:)
+      def self.from_items(value, mode:, type:)
         value.freeze
         return value if mode == :plain
 
-        new(value, member_types: member_types)
+        new(value, type: type)
       end
 
       # @param value [::Object] (#size, #each)
-      # @param member_types [::Array<Type>]
+      # @param type [Type]
       # @return [Struct]
-      def self.from_typed(value, member_types:)
+      def self.from_typed(value, type:)
         # TODO: validation
+        member_types = type.members
         raise unless value.size == member_types.size
 
         items = member_types.zip(value).map do |item_type, item|
           Data.make_typed(item_type, item)
         end
 
-        new(items, member_types: member_types) # 
initialize(::Array<Data::Base>)
+        new(items, type: type) # initialize(::Array<Data::Base>)
       end
 
-      def initialize(value, member_types:)
-        @member_types = member_types
-        @type = nil
+      def initialize(value, type:)
+        self.class.assert_type_matches_class(type)
+        @type = type
         super(value)
       end
     end
@@ -634,10 +626,10 @@
       end
 
       # @param value [::Object]
-      # @param member_types [::Array<Type>]
+      # @param type [Type]
       # @return [Variant]
-      def self.from_typed(value, member_types:) # rubocop:disable 
Lint/UnusedMethodArgument
-        # assert member_types.empty?
+      def self.from_typed(value, type:)
+        assert_type_matches_class(type)
 
         # decide on type of value
         new(value, member_type: nil)
@@ -690,31 +682,19 @@
         8
       end
 
-      # @return [::Array<Type>]
-      attr_reader :member_types
-
-      def type
-        return @type if @type
-
-        # TODO: reconstructing the type is cumbersome; have #initialize take 
*type* instead?
-        @type = Type.new(self.class.type_code, abstract: true)
-        @member_types.each do |member_type|
-          @type << member_type
-        end
-        @type
-      end
-
       # @param value [::Array]
-      def self.from_items(value, mode:, member_types:) # rubocop:disable 
Lint/UnusedMethodArgument
+      def self.from_items(value, mode:, type:) # rubocop:disable 
Lint/UnusedMethodArgument
         value.freeze
         # DictEntry ignores the :exact mode
         value
       end
 
       # @param value [::Object] (#size, #each)
-      # @param member_types [::Array<Type>]
+      # @param type [Type]
       # @return [DictEntry]
-      def self.from_typed(value, member_types:)
+      def self.from_typed(value, type:)
+        assert_type_matches_class(type)
+        member_types = type.members
         # assert member_types.size == 2
         # TODO: duplicated from Struct. Inherit/delegate?
         # TODO: validation
@@ -724,12 +704,12 @@
           Data.make_typed(item_type, item)
         end
 
-        new(items, member_types: member_types) # 
initialize(::Array<Data::Base>)
+        new(items, type: type) # initialize(::Array<Data::Base>)
       end
 
-      def initialize(value, member_types:)
-        @member_types = member_types
-        @type = nil
+      def initialize(value, type:)
+        self.class.assert_type_matches_class(type)
+        @type = type
         super(value)
       end
     end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/dbus/marshall.rb new/lib/dbus/marshall.rb
--- old/lib/dbus/marshall.rb    2022-04-21 13:44:41.000000000 +0200
+++ new/lib/dbus/marshall.rb    2022-04-27 10:47:47.000000000 +0200
@@ -118,7 +118,7 @@
           values = signature.members.map do |child_sig|
             do_parse(child_sig, mode: mode)
           end
-          packet = data_class.from_items(values, mode: mode, member_types: 
signature.members)
+          packet = data_class.from_items(values, mode: mode, type: signature)
 
         when Type::VARIANT
           data_sig = do_parse(Data::Signature.type, mode: :exact) # -> 
Data::Signature
@@ -147,7 +147,7 @@
             items << item
           end
           is_hash = signature.child.sigtype == Type::DICT_ENTRY
-          packet = data_class.from_items(items, mode: mode, member_type: 
signature.child, hash: is_hash)
+          packet = data_class.from_items(items, mode: mode, type: signature, 
hash: is_hash)
         end
       end
       packet
@@ -282,8 +282,11 @@
 
     def append_variant(val)
       vartype = nil
-      if val.is_a?(DBus::Data::Base)
-        vartype = val.type # FIXME: box or unbox another variant?
+      if val.is_a?(DBus::Data::Variant)
+        vartype = val.member_type
+        vardata = val.value
+      elsif val.is_a?(DBus::Data::Base)
+        vartype = val.type
         vardata = val.value
       elsif val.is_a?(Array) && val.size == 2
         case val[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/dbus/type.rb new/lib/dbus/type.rb
--- old/lib/dbus/type.rb        2022-04-21 13:44:41.000000000 +0200
+++ new/lib/dbus/type.rb        2022-04-27 10:47:47.000000000 +0200
@@ -29,14 +29,12 @@
   # For documentation purposes only.
   class Prototype < String; end
 
-  # = D-Bus type module
-  #
-  # This module containts the constants of the types specified in the D-Bus
-  # protocol.
+  # Represents the D-Bus types.
   #
   # Corresponds to {SingleCompleteType}.
+  # Instances are immutable/frozen once fully constructed.
   #
-  # See also {DBus::Data::Signature}
+  # See also {DBus::Data::Signature} which is "type on the wire".
   class Type
     # Mapping from type number to name and alignment.
     TYPE_MAPPING = {
@@ -104,8 +102,9 @@
         end
       end
 
-      @sigtype = sigtype
-      @members = []
+      @sigtype = sigtype.freeze
+      @members = [] # not frozen yet, Parser#parse_one or Factory will do it
+      freeze
     end
 
     # Return the required alignment for the type.
@@ -124,16 +123,15 @@
       when DICT_ENTRY
         "{#{@members.collect(&:to_s).join}}"
       else
-        if !TYPE_MAPPING.keys.member?(@sigtype)
-          raise NotImplementedError
-        end
-
         @sigtype.chr
       end
     end
 
     # Add a new member type _item_.
+    # @param item [Type]
     def <<(item)
+      raise ArgumentError unless item.is_a?(Type)
+
       if ![STRUCT, ARRAY, DICT_ENTRY].member?(@sigtype)
         raise SignatureException
       end
@@ -232,6 +230,7 @@
         else
           res = Type.new(char)
         end
+        res.members.freeze
         res
       end
 
@@ -243,7 +242,7 @@
         while (c = nextchar)
           ret << parse_one(c)
         end
-        ret
+        ret.freeze
       end
 
       # Parse one {SingleCompleteType}
@@ -255,9 +254,116 @@
         t = parse_one(c)
         raise SignatureException, "Has more than a Single Complete Type: 
#{@signature}" unless nextchar.nil?
 
+        t.freeze
+      end
+    end
+
+    class Factory
+      # @param type [Type,SingleCompleteType,Class]
+      # @see from_plain_class
+      # @return [Type] (frozen)
+      def self.make_type(type)
+        case type
+        when Type
+          type
+        when String
+          DBus.type(type)
+        when Class
+          from_plain_class(type)
+        else
+          msg = "Expecting DBus::Type, DBus::SingleCompleteType(aka ::String), 
or Class, got #{type.inspect}"
+          raise ArgumentError, msg
+        end
+      end
+
+      # Make a {Type} corresponding to some plain classes:
+      # - String
+      # - Float
+      # - DBus::ObjectPath
+      # - DBus::Signature, DBus::SingleCompleteType
+      # @param klass [Class]
+      # @return [Type] (frozen)
+      def self.from_plain_class(klass)
+        @signature_type ||= DBus.type(SIGNATURE)
+        @class_to_type ||= {
+          DBus::ObjectPath => DBus.type(OBJECT_PATH),
+          DBus::Signature => @signature_type,
+          DBus::SingleCompleteType => @signature_type,
+          String => DBus.type(STRING),
+          Float => DBus.type(DOUBLE)
+        }
+        t = @class_to_type[klass]
+        raise ArgumentError, "Cannot convert plain class #{klass} to a D-Bus 
type" if t.nil?
+
+        t
+      end
+    end
+
+    # Syntactic helper for constructing an array Type.
+    # You may be looking for {Data::Array} instead.
+    # @example
+    #   t = Type::Array[Type::INT16]
+    class ArrayFactory < Factory
+      # @param member_type [Type,SingleCompleteType]
+      # @return [Type] (frozen)
+      def self.[](member_type)
+        t = Type.new(ARRAY)
+        t << make_type(member_type)
+        t.members.freeze
+        t
+      end
+    end
+
+    # @example
+    #   t = Type::Array[Type::INT16]
+    Array = ArrayFactory
+
+    # Syntactic helper for constructing a hash Type.
+    # You may be looking for {Data::Array} and {Data::DictEntry} instead.
+    # @example
+    #   t = Type::Hash[Type::STRING, Type::VARIANT]
+    class HashFactory < Factory
+      # @param key_type [Type,SingleCompleteType]
+      # @param value_type [Type,SingleCompleteType]
+      # @return [Type] (frozen)
+      def self.[](key_type, value_type)
+        t = Type.new(ARRAY)
+        de = Type.new(DICT_ENTRY, abstract: true)
+        de << make_type(key_type)
+        de << make_type(value_type)
+        de.members.freeze
+        t << de
+        t.members.freeze
         t
       end
     end
+
+    # @example
+    #   t = Type::Hash[Type::INT16]
+    Hash = HashFactory
+
+    # Syntactic helper for constructing a struct Type.
+    # You may be looking for {Data::Struct} instead.
+    # @example
+    #   t = Type::Struct[Type::INT16, Type::STRING]
+    class StructFactory < Factory
+      # @param member_types [::Array<Type,SingleCompleteType>]
+      # @return [Type] (frozen)
+      def self.[](*member_types)
+        raise ArgumentError if member_types.empty?
+
+        t = Type.new(STRUCT, abstract: true)
+        member_types.each do |mt|
+          t << make_type(mt)
+        end
+        t.members.freeze
+        t
+      end
+    end
+
+    # @example
+    #   t = Type::Struct[Type::INT16, Type::STRING]
+    Struct = StructFactory
   end
 
   # shortcuts
@@ -266,7 +372,7 @@
   # This is prefered to {Type#initialize} which allows
   # incomplete or invalid types.
   # @param string_type [SingleCompleteType]
-  # @return [DBus::Type]
+  # @return [DBus::Type] (frozen)
   # @raise SignatureException
   def type(string_type)
     Type::Parser.new(string_type).parse1
@@ -275,7 +381,7 @@
 
   # Parse a String to zero or more {DBus::Type}s.
   # @param string_type [Signature]
-  # @return [Array<DBus::Type>]
+  # @return [Array<DBus::Type>] (frozen)
   # @raise SignatureException
   def types(string_type)
     Type::Parser.new(string_type).parse
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata        2022-04-21 13:44:41.000000000 +0200
+++ new/metadata        2022-04-27 10:47:47.000000000 +0200
@@ -1,14 +1,14 @@
 --- !ruby/object:Gem::Specification
 name: ruby-dbus
 version: !ruby/object:Gem::Version
-  version: 0.18.0.beta4
+  version: 0.18.0.beta5
 platform: ruby
 authors:
 - Ruby DBus Team
 autorequire: 
 bindir: bin
 cert_chain: []
-date: 2022-04-21 00:00:00.000000000 Z
+date: 2022-04-27 00:00:00.000000000 Z
 dependencies:
 - !ruby/object:Gem::Dependency
   name: rexml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/data_spec.rb new/spec/data_spec.rb
--- old/spec/data_spec.rb       2022-04-21 13:44:41.000000000 +0200
+++ new/spec/data_spec.rb       2022-04-27 10:47:47.000000000 +0200
@@ -85,6 +85,8 @@
 # TODO: Look at conversions? to_str, to_int?
 
 describe DBus::Data do
+  T = DBus::Type unless const_defined? "T"
+
   # test initialization, from user code, or from packet (from_raw)
   # remember to unpack if initializing from Data::Base
   # #value should recurse inside so that the user doesnt have to
@@ -245,18 +247,18 @@
   describe "containers" do
     describe DBus::Data::Array do
       good = [
-        # [[1, 2, 3], member_type: nil],
-        [[1, 2, 3], { member_type: "q" }],
-        [[1, 2, 3], { member_type: DBus::Type::UINT16 }],
-        [[1, 2, 3], { member_type: DBus.type("q") }],
-        [[DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), 
DBus::Data::UInt16.new(3)], { member_type: "q" }]
+        # [[1, 2, 3], type: nil],
+        [[1, 2, 3], { type: "aq" }],
+        [[1, 2, 3], { type: T::Array[T::UINT16] }],
+        [[1, 2, 3], { type: T::Array["q"] }],
+        [[DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), 
DBus::Data::UInt16.new(3)], { type: T::Array["q"] }]
         # TODO: others
       ]
 
       bad = [
         # undesirable type guessing
-        ## [[1, 2, 3], { member_type: nil }, DBus::InvalidPacketException, 
"Unknown type code"],
-        ## [[1, 2, 3], { member_type: "!" }, DBus::InvalidPacketException, 
"Unknown type code"]
+        ## [[1, 2, 3], { type: nil }, DBus::InvalidPacketException, "Unknown 
type code"],
+        ## [[1, 2, 3], { type: "!" }, DBus::InvalidPacketException, "Unknown 
type code"]
         # TODO: others
       ]
 
@@ -265,8 +267,8 @@
 
       describe ".from_typed" do
         it "creates new instance from given object and type" do
-          type = DBus::Type.new("s")
-          expect(described_class.from_typed(["test", "lest"], member_types: 
[type])).to be_a(described_class)
+          type = T::Array[String]
+          expect(described_class.from_typed(["test", "lest"], type: type)).to 
be_a(described_class)
         end
       end
     end
@@ -274,7 +276,7 @@
     describe DBus::Data::Struct do
       three_words = ::Struct.new(:a, :b, :c)
 
-      qqq = ["q", "q", "q"]
+      qqq = T::Struct[T::UINT16, T::UINT16, T::UINT16]
       integers = [1, 2, 3]
       uints = [DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), 
DBus::Data::UInt16.new(3)]
 
@@ -291,28 +293,25 @@
       # TODO: also check data ownership: reasonable to own the data?
       # can make it explicit?
       good = [
-        # from plain array; various m_t styles
-        [integers, { member_types: ["q", "q", "q"] }],
-        [integers, { member_types: [DBus::Type::UINT16, DBus::Type::UINT16, 
DBus::Type::UINT16] }],
-        [integers, { member_types: DBus.types("qqq") }],
+        # from plain array; various *type* styles
+        [integers, { type: DBus.type("(qqq)") }],
+        [integers, { type: T::Struct["q", "q", "q"] }],
+        [integers, { type: T::Struct[T::UINT16, T::UINT16, T::UINT16] }],
+        [integers, { type: T::Struct[*DBus.types("qqq")] }],
         # plain array of data
-        [uints, { member_types: DBus.types("qqq") }],
+        [uints, { type: qqq }],
         # ::Struct
-        [three_words.new(*integers), { member_types: qqq }],
-        [three_words.new(*uints), { member_types: qqq }]
+        [three_words.new(*integers), { type: qqq }],
+        [three_words.new(*uints), { type: qqq }]
         # TODO: others
       ]
 
+      # check these only when canonicalizing @value, because that will
+      # type-check the value deeply
       _bad_but_valid = [
-        # Wrong member_types arg:
-        # hmm this is another reason to pass the type
-        # as the entire struct type, not the members:
-        # empty struct will be caught naturally
-        [integers, { member_types: [] }, ArgumentError, "???"],
-        [integers, { member_types: ["!"] }, DBus::InvalidPacketException, 
"Unknown type code"],
         # STRUCT specific: member count mismatch
-        [[1, 2], { member_types: DBus.types("qqq") }, ArgumentError, "???"],
-        [[1, 2, 3, 4], { member_types: DBus.types("qqq") }, ArgumentError, 
"???"]
+        [[1, 2], { type: qqq }, ArgumentError, "???"],
+        [[1, 2, 3, 4], { type: qqq }, ArgumentError, "???"]
         # TODO: others
       ]
 
@@ -321,8 +320,8 @@
 
       describe ".from_typed" do
         it "creates new instance from given object and type" do
-          type = DBus::Type.new("s")
-          expect(described_class.from_typed(["test", "lest"].freeze, 
member_types: [type, type]))
+          type = T::Struct[T::STRING, T::STRING]
+          expect(described_class.from_typed(["test", "lest"].freeze, type: 
type))
             .to be_a(described_class)
         end
       end
@@ -331,16 +330,9 @@
     describe DBus::Data::Variant do
       describe ".from_typed" do
         it "creates new instance from given object and type" do
-          type = DBus::Type.new("s")
-          expect(described_class.from_typed("test", member_types: [type])).to 
be_a(described_class)
-        end
-
-        it "ignores the member_types argument" do
-          type = DBus::Type.new("s")
-          # Base.from_typed is a generic interface with a fixed signature;
-          # So it must offer the member_types parameter, which is misleading
-          # for a Variant
-          value = described_class.from_typed("test", member_types: [type])
+          type = DBus.type(T::VARIANT)
+          value = described_class.from_typed("test", type: type)
+          expect(value).to be_a(described_class)
           expect(value.type.to_s).to eq "v"
           expect(value.member_type.to_s).to eq "s"
         end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/property_spec.rb new/spec/property_spec.rb
--- old/spec/property_spec.rb   2022-04-21 13:44:41.000000000 +0200
+++ new/spec/property_spec.rb   2022-04-27 10:47:47.000000000 +0200
@@ -163,7 +163,7 @@
     end
   end
 
-  context "an dict-typed property" do
+  context "a dict-typed property" do
     it "gets read as a hash" do
       val = @iface["MyDict"]
       expect(val).to eq({
@@ -172,6 +172,23 @@
                           "three" => [3, 3, 3]
                         })
     end
+
+    it "Get returns the correctly typed value (check with dbus-send)" do
+      cmd = "dbus-send --print-reply " \
+            "--dest=org.ruby.service " \
+            "/org/ruby/MyInstance " \
+            "org.freedesktop.DBus.Properties.Get " \
+            "string:org.ruby.SampleInterface " \
+            "string:MyDict"
+      reply = `#{cmd}`
+      # a bug about variant nesting lead to a "variant variant int32 1" value
+      match_rx = /variant \s+ array \s \[ \s+
+         dict \s entry\( \s+
+            string \s "one" \s+
+            variant \s+ int32 \s 1 \s+
+         \)/x
+      expect(reply).to match(match_rx)
+    end
   end
 
   context "a variant-typed property" do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spec/type_spec.rb new/spec/type_spec.rb
--- old/spec/type_spec.rb       2022-04-21 13:44:41.000000000 +0200
+++ new/spec/type_spec.rb       2022-04-27 10:47:47.000000000 +0200
@@ -45,6 +45,7 @@
       ["a{vs}", "DICT_ENTRY key must be basic (non-container)"],
       ["{sv}", "DICT_ENTRY not an immediate child of an ARRAY"],
       ["a({sv})", "DICT_ENTRY not an immediate child of an ARRAY"],
+      ["a{s", "DICT_ENTRY not closed"],
       ["a{sv", "DICT_ENTRY not closed"],
       ["}", "DICT_ENTRY unexpectedly closed"],
 
@@ -78,5 +79,109 @@
         end
       end
     end
+  end
+
+  describe DBus::Type do
+    describe "#<<" do
+      it "raises if the argument is not a Type" do
+        t = DBus::Type.new(DBus::Type::ARRAY)
+        expect { t << "s" }.to raise_error(ArgumentError)
+      end
+
+      # TODO: the following raise checks do not occur in practice, as there are
+      # parallel checks in the parses. The code could be simplified?
+      it "raises if adding too much to an array" do
+        t = DBus::Type.new(DBus::Type::ARRAY)
+        b = DBus::Type.new(DBus::Type::BOOLEAN)
+        t << b
+        expect { t << b }.to raise_error(DBus::Type::SignatureException)
+      end
+
+      it "raises if adding too much to a dict_entry" do
+        t = DBus::Type.new(DBus::Type::DICT_ENTRY, abstract: true)
+        b = DBus::Type.new(DBus::Type::BOOLEAN)
+        t << b
+        t << b
+        expect { t << b }.to raise_error(DBus::Type::SignatureException)
+      end
+
+      it "raises if adding to a non-container" do
+        t = DBus::Type.new(DBus::Type::STRING)
+        b = DBus::Type.new(DBus::Type::BOOLEAN)
+        expect { t << b }.to raise_error(DBus::Type::SignatureException)
+
+        t = DBus::Type.new(DBus::Type::VARIANT)
+        expect { t << b }.to raise_error(DBus::Type::SignatureException)
+      end
+    end
+
+    describe DBus::Type::Array do
+      describe ".[]" do
+        it "takes Type argument" do
+          t = DBus::Type::Array[DBus::Type.new("s")]
+          expect(t.to_s).to eq "as"
+        end
+
+        it "takes 's':String argument" do
+          t = DBus::Type::Array["s"]
+          expect(t.to_s).to eq "as"
+        end
+
+        it "takes String:Class argument" do
+          t = DBus::Type::Array[String]
+          expect(t.to_s).to eq "as"
+        end
+
+        it "rejects Integer:Class argument" do
+          expect { DBus::Type::Array[Integer] }.to raise_error(ArgumentError)
+        end
+
+        it "rejects /./:Regexp argument" do
+          expect { DBus::Type::Array[/./] }.to raise_error(ArgumentError)
+        end
+      end
+    end
+
+    describe DBus::Type::Hash do
+      describe ".[]" do
+        it "takes Type arguments" do
+          t = DBus::Type::Hash[DBus::Type.new("s"), DBus::Type.new("v")]
+          expect(t.to_s).to eq "a{sv}"
+        end
+
+        it "takes 's':String arguments" do
+          t = DBus::Type::Hash["s", "v"]
+          expect(t.to_s).to eq "a{sv}"
+        end
+
+        it "takes String:Class argument" do
+          t = DBus::Type::Hash[String, DBus::Type::VARIANT]
+          expect(t.to_s).to eq "a{sv}"
+        end
+      end
+    end
+
+    describe DBus::Type::Struct do
+      describe ".[]" do
+        it "takes Type arguments" do
+          t = DBus::Type::Struct[DBus::Type.new("s"), DBus::Type.new("v")]
+          expect(t.to_s).to eq "(sv)"
+        end
+
+        it "takes 's':String arguments" do
+          t = DBus::Type::Struct["s", "v"]
+          expect(t.to_s).to eq "(sv)"
+        end
+
+        it "takes String:Class argument" do
+          t = DBus::Type::Struct[String, DBus::Type::VARIANT]
+          expect(t.to_s).to eq "(sv)"
+        end
+
+        it "raises on no arguments" do
+          expect { DBus::Type::Struct[] }.to raise_error(ArgumentError)
+        end
+      end
+    end
   end
 end

Reply via email to