File: object_spec.rb

package info (click to toggle)
ruby-dbus 0.25.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 776 kB
  • sloc: ruby: 6,584; xml: 225; sh: 38; makefile: 8
file content (194 lines) | stat: -rwxr-xr-x 5,582 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/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

    it "fails when the signature is invalid" do
      expect do
        ObjectTest.instance_exec do
          dbus_interface "org.ruby.ServerTest" do
            dbus_reader :foo2, "!"
          end
        end
      end.to raise_error(DBus::Type::SignatureException)
    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 ".dbus_signal" do
    it "can only be used within a dbus_interface" do
      expect do
        ObjectTest.instance_exec do
          dbus_signal :signal_without_interface
        end
      end.to raise_error(DBus::Object::UndefinedInterface)
    end

    it "cannot be named with a bang" do
      expect do
        ObjectTest.instance_exec do
          dbus_interface "org.ruby.ServerTest" do
            # a valid Ruby symbol but an invalid DBus name; Ticket#38
            dbus_signal :signal_with_a_bang!
          end
        end
      end.to raise_error(DBus::InvalidMethodName)
    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

  # coverage obsession
  describe "#dispatch" do
    it "survives being called with a non-METHOD_CALL, doing nothing" do
      obj = ObjectTest.new("/test")
      msg = DBus::MethodReturnMessage.new
      expect { obj.dispatch(msg) }.to_not raise_error
    end
  end

  describe "#emit" do
    context "before the object has been exported" do
      it "raises an explanatory error" do
        obj = ObjectTest.new("/test")

        intf = DBus::Interface.new("org.example.Test")
        signal = DBus::Signal.new("Ring")
        expect { obj.emit(intf, signal) }
          .to raise_error(
            RuntimeError,
            %r{Cannot emit signal org.example.Test.Ring before /test is exported}
          )
      end
    end
  end
end