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-06-23 10:22:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-ruby-dbus (Old) and /work/SRC/openSUSE:Factory/.rubygem-ruby-dbus.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-ruby-dbus" Thu Jun 23 10:22:13 2022 rev:30 rq:984133 version:0.18.0.beta8 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-ruby-dbus/rubygem-ruby-dbus.changes 2022-05-30 12:42:14.760254430 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-ruby-dbus.new.1548/rubygem-ruby-dbus.changes 2022-06-23 10:22:16.607610013 +0200 @@ -1,0 +2,16 @@ +Tue Jun 21 09:54:22 UTC 2022 - Martin Vidner <mvid...@suse.com> + +- 0.18.0.beta8 + Bug fixes: + * Introduced Object#dbus_properties_changed to send correctly typed property + values (gh#mvidner/ruby-dbus#115). Avoid calling PropertiesChanged directly + as it will guess the types. + * Fix Object.dbus_reader to work with attr_accessor and automatically produce + dbus_properties_changed for properties that are read-write at + implementation side and read-only at D-Bus side (gh#mvidner/ruby-dbus#96) + API: + * Service side `emits_changed_signal` to control emission of + PropertiesChanged: can be assigned within `dbus_interface` or as an option + when declaring properties (gh#mvidner/ruby-dbus#117). + +------------------------------------------------------------------- Old: ---- ruby-dbus-0.18.0.beta7.gem New: ---- ruby-dbus-0.18.0.beta8.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-ruby-dbus.spec ++++++ --- /var/tmp/diff_new_pack.TxXGBJ/_old 2022-06-23 10:22:17.023610465 +0200 +++ /var/tmp/diff_new_pack.TxXGBJ/_new 2022-06-23 10:22:17.027610470 +0200 @@ -24,7 +24,7 @@ # Name: rubygem-ruby-dbus -Version: 0.18.0.beta7 +Version: 0.18.0.beta8 Release: 0 %define mod_name ruby-dbus %define mod_full_name %{mod_name}-%{version} ++++++ ruby-dbus-0.18.0.beta7.gem -> ruby-dbus-0.18.0.beta8.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/NEWS.md new/NEWS.md --- old/NEWS.md 2022-05-29 10:48:54.000000000 +0200 +++ new/NEWS.md 2022-06-21 12:04:46.000000000 +0200 @@ -2,6 +2,26 @@ ## Unreleased +## Ruby D-Bus 0.18.0.beta8 - 2022-06-21 + +Bug fixes: + * Introduced Object#dbus_properties_changed to send correctly typed property + values ([#115][]). Avoid calling PropertiesChanged directly as it will + guess the types. + * Fix Object.dbus_reader to work with attr_accessor and automatically produce + dbus_properties_changed for properties that are read-write at + implementation side and read-only at D-Bus side ([#96][]) + +[#96]: https://github.com/mvidner/ruby-dbus/issues/96 + +API: + * Service side `emits_changed_signal` to control emission of + PropertiesChanged: can be assigned within `dbus_interface` or as an option + when declaring properties ([#117][]). + +[#115]: https://github.com/mvidner/ruby-dbus/issues/115 +[#117]: https://github.com/mvidner/ruby-dbus/pulls/117 + ## Ruby D-Bus 0.18.0.beta7 - 2022-05-29 API: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/VERSION new/VERSION --- old/VERSION 2022-05-29 10:48:54.000000000 +0200 +++ new/VERSION 2022-06-21 12:04:46.000000000 +0200 @@ -1 +1 @@ -0.18.0.beta7 +0.18.0.beta8 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/bus.rb new/lib/dbus/bus.rb --- old/lib/dbus/bus.rb 2022-05-29 10:48:54.000000000 +0200 +++ new/lib/dbus/bus.rb 2022-06-21 12:04:46.000000000 +0200 @@ -171,15 +171,11 @@ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> ' xml += "<node name=\"#{node_opath}\">\n" - each_pair do |k, _v| + each_key do |k| xml += " <node name=\"#{k}\" />\n" end - @object&.intfs&.each_pair do |_k, v| - xml += " <interface name=\"#{v.name}\">\n" - v.methods.each_value { |m| xml += m.to_xml } - v.signals.each_value { |m| xml += m.to_xml } - v.properties.each_value { |m| xml += m.to_xml } - xml += " </interface>\n" + @object&.intfs&.each_value do |v| + xml += v.to_xml end xml += "</node>" xml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/dbus/emits_changed_signal.rb new/lib/dbus/emits_changed_signal.rb --- old/lib/dbus/emits_changed_signal.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/dbus/emits_changed_signal.rb 2022-06-21 12:04:46.000000000 +0200 @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +# This file is part of the ruby-dbus project +# Copyright (C) 2022 Martin Vidner +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License, version 2.1 as published by the Free Software Foundation. +# See the file "COPYING" for the exact licensing terms. + +module DBus + # Describes the behavior of PropertiesChanged signal, for a single property + # or for an entire interface. + # + # The possible values are: + # + # - *true*: the signal is emitted with the value included. + # - *:invalidates*: the signal is emitted but the value is not included + # in the signal. + # - *:const*: the property never changes value during the lifetime + # of the object it belongs to, and hence the signal + # is never emitted for it (but clients can cache the value) + # - *false*: the signal won't be emitted (clients should re-Get the property value) + # + # The default is: + # - for an interface: *true* + # - for a property: what the parent interface specifies + # + # @see DBus::Object.emits_changed_signal + # @see DBus::Object.dbus_attr_accessor + # @see https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format + # + # Immutable once constructed. + class EmitsChangedSignal + # @return [true,false,:const,:invalidates] + attr_reader :value + + # @param value [true,false,:const,:invalidates,nil] + # See class-level description above, {EmitsChangedSignal}. + # @param interface [Interface,nil] + # If the (property-level) *value* is unspecified (nil), this is the + # containing {Interface} to get the value from. + def initialize(value, interface: nil) + if value.nil? + raise ArgumentError, "Both arguments are nil" if interface.nil? + + @value = interface.emits_changed_signal.value + else + expecting = [true, false, :const, :invalidates] + unless expecting.include?(value) + raise ArgumentError, "Expecting one of #{expecting.inspect}. Seen #{value.inspect}" + end + + @value = value + end + + freeze + end + + # Return introspection XML string representation + # @return [String] + def to_xml + return "" if @value == true + + " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"#{@value}\"/>\n" + end + + def to_s + @value.to_s + end + + def ==(other) + if other.is_a?(self.class) + other.value == @value + else + other == value + end + end + alias eql? == + + DEFAULT_ECS = EmitsChangedSignal.new(true) + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/dbus/introspect.rb new/lib/dbus/introspect.rb --- old/lib/dbus/introspect.rb 2022-05-29 10:48:54.000000000 +0200 +++ new/lib/dbus/introspect.rb 2022-06-21 12:04:46.000000000 +0200 @@ -26,7 +26,7 @@ # method call instantiates and configures this class for us. # # It also is the local definition of interface exported by the program. - # At the client side, see ProxyObjectInterface + # At the client side, see {ProxyObjectInterface}. class Interface # @return [String] The name of the interface. attr_reader :name @@ -38,6 +38,9 @@ # @return [Hash{Symbol => Property}] attr_reader :properties + # @return [EmitsChangedSignal] + attr_reader :emits_changed_signal + # Creates a new interface with a given _name_. def initialize(name) validate_name(name) @@ -45,6 +48,20 @@ @methods = {} @signals = {} @properties = {} + @emits_changed_signal = EmitsChangedSignal::DEFAULT_ECS + end + + # Helper for {Object.emits_changed_signal=}. + # @api private + def emits_changed_signal=(ecs) + raise TypeError unless ecs.is_a? EmitsChangedSignal + # equal?: object identity + unless @emits_changed_signal.equal?(EmitsChangedSignal::DEFAULT_ECS) || + @emits_changed_signal.value == ecs.value + raise "emits_change_signal was assigned more than once" + end + + @emits_changed_signal = ecs end # Validates a service _name_. @@ -85,6 +102,18 @@ define(m) end alias declare_method define_method + + # Return introspection XML string representation of the property. + # @return [String] + def to_xml + xml = " <interface name=\"#{name}\">\n" + xml += emits_changed_signal.to_xml + methods.each_value { |m| xml += m.to_xml } + signals.each_value { |m| xml += m.to_xml } + properties.each_value { |m| xml += m.to_xml } + xml += " </interface>\n" + xml + end end # = A formal parameter has a name and a type @@ -190,6 +219,7 @@ end # Return an XML string representation of the method interface elment. + # @return [String] def to_xml xml = " <method name=\"#{@name}\">\n" @params.each do |param| diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/dbus/object.rb new/lib/dbus/object.rb --- old/lib/dbus/object.rb 2022-05-29 10:48:54.000000000 +0200 +++ new/lib/dbus/object.rb 2022-06-21 12:04:46.000000000 +0200 @@ -102,6 +102,18 @@ end end + # Declare the behavior of PropertiesChanged signal, + # common for all properties in this interface + # (individual properties may override it) + # @example + # self.emits_changed_signal = :invalidates + # @param [true,false,:const,:invalidates] value + def self.emits_changed_signal=(value) + raise UndefinedInterface, :emits_changed_signal if @@cur_intf.nil? + + @@cur_intf.emits_changed_signal = EmitsChangedSignal.new(value) + end + # A read-write property accessing an instance variable. # A combination of `attr_accessor` and {.dbus_accessor}. # @@ -114,11 +126,13 @@ # @param dbus_name [String] if not given it is made # by CamelCasing the ruby_name. foo_bar becomes FooBar # to convert the Ruby convention to the DBus convention. + # @param emits_changed_signal [true,false,:const,:invalidates] + # see {EmitsChangedSignal}; if unspecified, ask the interface. # @return [void] - def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil) + def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) attr_accessor(ruby_name) - dbus_accessor(ruby_name, type, dbus_name: dbus_name) + dbus_accessor(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end # A read-only property accessing an instance variable. @@ -126,19 +140,20 @@ # # Whenever the property value gets changed from "inside" the object, # you should emit the `PropertiesChanged` signal by calling + # {#dbus_properties_changed}. # - # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, []) + # dbus_properties_changed(interface_name, {dbus_name.to_s => value}, []) # # or, omitting the value in the signal, # - # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s]) + # dbus_properties_changed(interface_name, {}, [dbus_name.to_s]) # # @param (see .dbus_attr_accessor) # @return (see .dbus_attr_accessor) - def self.dbus_attr_reader(ruby_name, type, dbus_name: nil) + def self.dbus_attr_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) attr_reader(ruby_name) - dbus_reader(ruby_name, type, dbus_name: dbus_name) + dbus_reader(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end # A write-only property accessing an instance variable. @@ -146,10 +161,10 @@ # # @param (see .dbus_attr_accessor) # @return (see .dbus_attr_accessor) - def self.dbus_attr_writer(ruby_name, type, dbus_name: nil) + def self.dbus_attr_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) attr_writer(ruby_name) - dbus_writer(ruby_name, type, dbus_name: dbus_name) + dbus_writer(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end # A read-write property using a pair of reader/writer methods @@ -160,36 +175,49 @@ # # @param (see .dbus_attr_accessor) # @return (see .dbus_attr_accessor) - def self.dbus_accessor(ruby_name, type, dbus_name: nil) + def self.dbus_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) raise UndefinedInterface, ruby_name if @@cur_intf.nil? dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name) property = Property.new(dbus_name, type, :readwrite, ruby_name: ruby_name) @@cur_intf.define(property) - dbus_watcher(ruby_name, dbus_name: dbus_name) + dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end # A read-only property accessing a reader method (which must already exist). # (To directly access an instance variable, use {.dbus_attr_reader} instead) # - # Whenever the property value gets changed from "inside" the object, + # At the D-Bus side the property is read only but it makes perfect sense to + # implement it with a read-write attr_accessor. In that case this method + # uses {.dbus_watcher} to set up the PropertiesChanged signal. + # + # attr_accessor :foo_bar + # dbus_reader :foo_bar, "s" + # + # If the property value should change by other means than its attr_writer, # you should emit the `PropertiesChanged` signal by calling + # {#dbus_properties_changed}. # - # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, []) + # dbus_properties_changed(interface_name, {dbus_name.to_s => value}, []) # # or, omitting the value in the signal, # - # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s]) + # dbus_properties_changed(interface_name, {}, [dbus_name.to_s]) # # @param (see .dbus_attr_accessor) # @return (see .dbus_attr_accessor) - def self.dbus_reader(ruby_name, type, dbus_name: nil) + def self.dbus_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) raise UndefinedInterface, ruby_name if @@cur_intf.nil? dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name) property = Property.new(dbus_name, type, :read, ruby_name: ruby_name) @@cur_intf.define(property) + + ruby_name_eq = "#{ruby_name}=".to_sym + return unless method_defined?(ruby_name_eq) + + dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end # A write-only property accessing a writer method (which must already exist). @@ -199,14 +227,14 @@ # # @param (see .dbus_attr_accessor) # @return (see .dbus_attr_accessor) - def self.dbus_writer(ruby_name, type, dbus_name: nil) + def self.dbus_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) raise UndefinedInterface, ruby_name if @@cur_intf.nil? dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name) property = Property.new(dbus_name, type, :write, ruby_name: ruby_name) @@cur_intf.define(property) - dbus_watcher(ruby_name, dbus_name: dbus_name) + dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end # Enables automatic sending of the PropertiesChanged signal. @@ -218,11 +246,13 @@ # @param dbus_name [String] if not given it is made # by CamelCasing the ruby_name. foo_bar becomes FooBar # to convert the Ruby convention to the DBus convention. + # @param emits_changed_signal [true,false,:const,:invalidates] + # see {EmitsChangedSignal}; if unspecified, ask the interface. # @return [void] - def self.dbus_watcher(ruby_name, dbus_name: nil) + def self.dbus_watcher(ruby_name, dbus_name: nil, emits_changed_signal: nil) raise UndefinedInterface, ruby_name if @@cur_intf.nil? - cur_intf = @@cur_intf + interface_name = @@cur_intf.name ruby_name = ruby_name.to_s.sub(/=$/, "").to_sym ruby_name_eq = "#{ruby_name}=".to_sym @@ -230,14 +260,27 @@ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name) + emits_changed_signal = EmitsChangedSignal.new(emits_changed_signal, interface: @@cur_intf) + # the argument order is alias_method(new_name, existing_name) alias_method original_ruby_name_eq, ruby_name_eq define_method ruby_name_eq do |value| - public_send(original_ruby_name_eq, value) + result = public_send(original_ruby_name_eq, value) - # TODO: respect EmitsChangedSignal to use invalidated_properties instead - # PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as" - PropertiesChanged(cur_intf.name, { dbus_name.to_s => value }, []) + case emits_changed_signal.value + when true + # signature: "interface:s, changed_props:a{sv}, invalidated_props:as" + dbus_properties_changed(interface_name, { dbus_name.to_s => value }, []) + when :invalidates + dbus_properties_changed(interface_name, {}, [dbus_name.to_s]) + when :const + # Oh my, seeing a value change of a supposedly constant property. + # Maybe should have raised at declaration time, don't make a fuss now. + when false + # Do nothing + end + + result end end @@ -301,6 +344,25 @@ dbus_name.to_sym end + # Use this instead of calling PropertiesChanged directly. This one + # considers not only the PC signature (which says that all property values + # are variants) but also the specific property type. + # @param interface_name [String] interface name like "org.example.ManagerManager" + # @param changed_props [Hash{String => ::Object}] + # changed properties (D-Bus names) and their values. + # @param invalidated_props [Array<String>] + # names of properties whose changed value is not specified + def dbus_properties_changed(interface_name, changed_props, invalidated_props) + typed_changed_props = changed_props.map do |dbus_name, value| + property = dbus_lookup_property(interface_name, dbus_name) + type = property.type + typed_value = Data.make_typed(type, value) + variant = Data::Variant.new(typed_value, member_type: type) + [dbus_name, variant] + end.to_h + PropertiesChanged(interface_name, typed_changed_props, invalidated_props) + end + # @param interface_name [String] # @param property_name [String] # @return [Property] 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-05-29 10:48:54.000000000 +0200 +++ new/lib/dbus/type.rb 2022-06-21 12:04:46.000000000 +0200 @@ -415,7 +415,7 @@ # @param string_type [SingleCompleteType] # @param value [::Object] # @return [Array(DBus::Type::Type,::Object)] - # @deprecated Use {Data::Variant.new} instead + # @deprecated Use {Data::Variant#initialize} instead def variant(string_type, value) Data::Variant.new(value, member_type: string_type) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/dbus.rb new/lib/dbus.rb --- old/lib/dbus.rb 2022-05-29 10:48:54.000000000 +0200 +++ new/lib/dbus.rb 2022-06-21 12:04:46.000000000 +0200 @@ -15,6 +15,7 @@ require_relative "dbus/bus" require_relative "dbus/bus_name" require_relative "dbus/data" +require_relative "dbus/emits_changed_signal" require_relative "dbus/error" require_relative "dbus/introspect" require_relative "dbus/logger" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2022-05-29 10:48:54.000000000 +0200 +++ new/metadata 2022-06-21 12:04:46.000000000 +0200 @@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: ruby-dbus version: !ruby/object:Gem::Version - version: 0.18.0.beta7 + version: 0.18.0.beta8 platform: ruby authors: - Ruby DBus Team -autorequire: +autorequire: bindir: bin cert_chain: [] -date: 2022-05-29 00:00:00.000000000 Z +date: 2022-06-21 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rexml @@ -146,6 +146,7 @@ - lib/dbus/core_ext/class/attribute.rb - lib/dbus/core_ext/module/redefine_method.rb - lib/dbus/data.rb +- lib/dbus/emits_changed_signal.rb - lib/dbus/error.rb - lib/dbus/introspect.rb - lib/dbus/logger.rb @@ -172,12 +173,14 @@ - spec/client_robustness_spec.rb - spec/data/marshall.yaml - spec/data_spec.rb +- spec/emits_changed_signal_spec.rb - spec/err_msg_spec.rb - spec/introspect_xml_parser_spec.rb - spec/introspection_spec.rb - spec/main_loop_spec.rb - spec/node_spec.rb - spec/object_path_spec.rb +- spec/object_spec.rb - spec/packet_marshaller_spec.rb - spec/packet_unmarshaller_spec.rb - spec/property_spec.rb @@ -202,7 +205,7 @@ licenses: - LGPL-2.1 metadata: {} -post_install_message: +post_install_message: rdoc_options: [] require_paths: - lib @@ -217,8 +220,9 @@ - !ruby/object:Gem::Version version: 1.3.1 requirements: [] -rubygems_version: 3.3.0.dev -signing_key: +rubyforge_project: +rubygems_version: 2.7.6.3 +signing_key: specification_version: 4 summary: Ruby module for interaction with D-Bus test_files: [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/emits_changed_signal_spec.rb new/spec/emits_changed_signal_spec.rb --- old/spec/emits_changed_signal_spec.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/spec/emits_changed_signal_spec.rb 2022-06-21 12:04:46.000000000 +0200 @@ -0,0 +1,58 @@ +#!/usr/bin/env rspec +# frozen_string_literal: true + +require_relative "spec_helper" +require "dbus" + +describe DBus::EmitsChangedSignal do + describe "#initialize" do + it "accepts a simple value" do + expect(described_class.new(:const).value).to eq :const + end + + it "avoids nil by asking the interface" do + ifc = DBus::Interface.new("org.example.Foo") + ifc.emits_changed_signal = described_class.new(:invalidates) + + expect(described_class.new(nil, interface: ifc).value).to eq :invalidates + end + + it "fails for unknown value" do + expect { described_class.new(:huh) }.to raise_error(ArgumentError, /Seen :huh/) + end + + it "fails for 2 nils" do + expect { described_class.new(nil, interface: nil) }.to raise_error(ArgumentError, /Both/) + end + end + + describe "#==" do + it "is true for two different objects with the same value" do + const_a = described_class.new(:const) + const_b = described_class.new(:const) + expect(const_a == const_b).to be true + end + end + + describe "#to_xml" do + it "uses a string value" do + expect(described_class.new(:const).to_xml) + .to eq " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n" + end + end + + describe "#to_s" do + it "uses a string value" do + expect(described_class.new(:const).to_s).to eq "const" + end + end +end + +describe DBus::Interface do + describe ".emits_changed_signal=" do + it "only allows an EmitsChangedSignal as argument" do + ifc = described_class.new("org.ruby.Interface") + expect { ifc.emits_changed_signal = :const }.to raise_error(TypeError) + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/object_spec.rb new/spec/object_spec.rb --- old/spec/object_spec.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/spec/object_spec.rb 2022-06-21 12:04:46.000000000 +0200 @@ -0,0 +1,138 @@ +#!/usr/bin/env rspec +# frozen_string_literal: true + +require_relative "spec_helper" +require "dbus" + +class ObjectTest < DBus::Object + T = DBus::Type unless const_defined? "T" + + dbus_interface "org.ruby.ServerTest" do + dbus_attr_writer :write_me, T::Struct[String, String] + + attr_accessor :read_only_for_dbus + + dbus_reader :read_only_for_dbus, T::STRING, emits_changed_signal: :invalidates + end +end + +describe DBus::Object do + describe ".dbus_attr_writer" do + describe "the declared assignment method" do + # Slightly advanced RSpec: + # https://rspec.info/documentation/3.9/rspec-expectations/RSpec/Matchers.html#satisfy-instance_method + let(:a_struct_in_a_variant) do + satisfying { |x| x.is_a?(DBus::Data::Variant) && x.member_type.to_s == "(ss)" } + # ^ This formatting keeps the matcher on a single line + # which enables RSpec to cite it if it fails, instead of saying "block". + end + + it "emits PropertyChanged with correctly typed argument" do + obj = ObjectTest.new("/test") + expect(obj).to receive(:PropertiesChanged).with( + "org.ruby.ServerTest", + { + "WriteMe" => a_struct_in_a_variant + }, + [] + ) + # bug: call PC with simply the assigned value, + # which will need type guessing + obj.write_me = ["two", "strings"] + end + end + end + + describe ".dbus_accessor" do + it "can only be used within a dbus_interface" do + expect do + ObjectTest.instance_exec do + dbus_accessor :foo, DBus::Type::STRING + end + end.to raise_error(DBus::Object::UndefinedInterface) + end + end + + describe ".dbus_reader" do + it "can only be used within a dbus_interface" do + expect do + ObjectTest.instance_exec do + dbus_reader :foo, DBus::Type::STRING + end + end.to raise_error(DBus::Object::UndefinedInterface) + end + end + + describe ".dbus_reader, when paired with attr_accessor" do + describe "the declared assignment method" do + it "emits PropertyChanged" do + obj = ObjectTest.new("/test") + expect(obj).to receive(:PropertiesChanged).with( + "org.ruby.ServerTest", + {}, + ["ReadOnlyForDbus"] + ) + obj.read_only_for_dbus = "myvalue" + end + end + end + + describe ".dbus_writer" do + it "can only be used within a dbus_interface" do + expect do + ObjectTest.instance_exec do + dbus_writer :foo, DBus::Type::STRING + end + end.to raise_error(DBus::Object::UndefinedInterface) + end + end + + describe ".dbus_watcher" do + it "can only be used within a dbus_interface" do + expect do + ObjectTest.instance_exec do + dbus_watcher :foo + end + end.to raise_error(DBus::Object::UndefinedInterface) + end + end + + describe ".dbus_method" do + it "can only be used within a dbus_interface" do + expect do + ObjectTest.instance_exec do + dbus_method :foo do + end + end + end.to raise_error(DBus::Object::UndefinedInterface) + end + end + + describe ".emits_changed_signal" do + it "raises UndefinedInterface when so" do + expect { ObjectTest.emits_changed_signal = false } + .to raise_error DBus::Object::UndefinedInterface + end + + it "assigns to the current interface" do + ObjectTest.instance_exec do + dbus_interface "org.ruby.Interface" do + self.emits_changed_signal = false + end + end + ecs = ObjectTest.intfs["org.ruby.Interface"].emits_changed_signal + expect(ecs).to eq false + end + + it "only can be assigned once" do + expect do + Class.new(DBus::Object) do + dbus_interface "org.ruby.Interface" do + self.emits_changed_signal = false + self.emits_changed_signal = :invalidates + end + end + end.to raise_error(RuntimeError, /assigned more than once/) + end + end +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-05-29 10:48:54.000000000 +0200 +++ new/spec/property_spec.rb 2022-06-21 12:04:46.000000000 +0200 @@ -110,6 +110,7 @@ end @iface["ReadOrWriteMe"] = "VALUE" + @iface.SetTwoProperties("REAMDE", 255) # loop to process the signal. complicated :-( see signal_spec.rb loop = DBus::Main.new @@ -123,6 +124,8 @@ quitter.join expect(received["ReadOrWriteMe"]).to eq("VALUE") + expect(received["ReadMe"]).to eq("REAMDE") + expect(received["MyByte"]).to eq(255) end context "a struct-typed property" do @@ -206,7 +209,7 @@ let(:a_byte_in_a_variant) do satisfying { |x| x.is_a?(DBus::Data::Variant) && x.member_type.to_s == DBus::Type::BYTE } # ^ This formatting keeps the matcher on a single line - # which enables RSpect to cite it if it fails, instead of saying "block". + # which enables RSpec to cite it if it fails, instead of saying "block". end let(:prop_iface) { @obj[DBus::PROPERTY_INTERFACE] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spec/service_newapi.rb new/spec/service_newapi.rb --- old/spec/service_newapi.rb 2022-05-29 10:48:54.000000000 +0200 +++ new/spec/service_newapi.rb 2022-06-21 12:04:46.000000000 +0200 @@ -115,6 +115,15 @@ dbus_attr_accessor :my_variant, "v" dbus_attr_accessor :my_byte, "y" + + # to test dbus_properties_changed + dbus_method :SetTwoProperties, "in read_me:s, in byte:y" do |read_me, byte| + @read_me = read_me + @my_byte = byte + dbus_properties_changed(INTERFACE, + { "ReadMe" => read_me, "MyByte" => byte }, + []) + end end # closing and reopening the same interface