Introducing Harald - The Ruby Bluetooth Tester
Thursday, January 22nd, 2009A Domain Specific Language in Ruby for Describing Monitoring and Control Operations on Bluetooth Adapters and Devices
- Contents
Micro-frameworks for Ruby-based unit testing are everywhere: there’s Test::Unit, RSpec, Shoulda and more.
In other domains such as web application development, there are also a variety of small but highly useful frameworks that have become popular: Sinatra, Merb, Camping, Wave.
Now there is Harald: a nano-framework for testing and monitoring Bluetooth adapters and devices on Linux. However, unlike the hulking Viking stature of its forebear, Harald Bluetooth, this Harald is rather sleek and well-mannered. But following the spirit of its namesake, this Harald (”the Bluetooth Tester”) seeks to simplify and at the same time unify the somewhat random collection of Bluetooth testing tools on Linux into a small Domain Specific Language (DSL).
This article presents a technical summary of the initial version of Harald. This version can be used to investigate, enumerate and test Bluetooth devices under Linux. It also incorporates a simple activity or message monitoring facility to track events and messages related to the Bluetooth adapters and devices attached to your system. Please see Building a Remote Message Monitor for the iPhone for an in-depth example of using this facility.
What is Harald?
Harald is a Ruby DSL for describing monitoring and control operations on Bluetooth adapters and devices in Linux. It is a simple language definition that enables the Bluetooth tester to enumerate components and query their properties. It uses a slightly customized Ruby D-Bus package to communicate cleanly with the BlueZ Bluetooth implementation on Linux. A script written using the Harald DSL consists of a sequence of Harald requests.
Getting Started
To display a listing of the properties of your Bluetooth system’s default adapter just enter the following lines into Ruby’s interactive shell irb or into a Ruby script file:
Listing 1: Show Properties
require 'rubygems' require 'harald' show "properties" do |result| pp result end
To display a listing of the devices (such as headsets, mice and keyboards) known to the current Bluetooth default adapter enter:
Listing 2: List Devices
# omitting requires list "devices", :label=>"my devices: " do |result| pp result end
And to change the alias property of a Bluetooth device enter:
Listing 3: Change Alias Property
test_alias = "alter_ego_for_my_device" set "device_alias", test_alias, :device_address=>"00:1A:45:6A:8B:4B", :label=>"changing alias to \"#{test_alias}\"\n" do |new_value| new_value.should_be test_alias end
Note the use of a ‘should_be‘ assertion clause in the preceding example. The assertion clause, or predicate, is used here to test the equivalence of the new alias value (’new_value‘) with the target value (’test_alias‘) required by the test case. If they are the same, Harald displays the following message:
OK, got expected value "alter_ego_for_my_device"
Grammar
A Harald program is a series of Harald requests, normally bracketed with test reporting instructions and incorporating test assertions or predicates. Each Harald request therefore can be viewed as a simple test case and a series of requests can be regarded as a test scenario.
Listing 4: Harald Program Structure
reset :tests Harald request 1 Harald request 2 . . . Harald request n report :passed
The format of a Harald request may be either
<VERB> <PROPERTY> [,<QUALIFIERS>] [<BLOCK>](for get style requests)
or
<VERB> <PROPERTY>, <VALUE> [,<QUALIFIERS>] [<BLOCK>](for set style requests)
depending on whether the it is a get style or a set style request.
VERB and PROPERTY are always required (unless the request is monitor). VALUE is required for set and forbidden for other requests. The other fields are optional. When BLOCK is expanded, the resulting Harald syntax looks like this:
Listing 5: Format of Harald Request
<VERB> <PROPERTY> [,<VALUE>] [,<QUALIFIERS>] [do |result| result.<PREDICATE> <some_value>] end
where,
| VERB: | The verb field indicates the type of action to be executed. Harald defines action verbs (show, list, set, and monitor) for investigating and manipulating the properties of Bluetooth adapters and devices. Harald also supports a simple test framework using additional verbs such as report and reset. |
| PROPERTY: | Properties are normally attributes of a Bluetooth component which, in this version of the Harald DSL, can be either a Bluetooth adapter, like a USB dongle, or a device, like a headphone or keyboard. In some cases, however, the property field contains a request modifier instead of an attribute. The Harald requests that use this field as a modifier include report, reset, and list. |
| VALUE: | The value field provides the value to be assigned to a property by the set request. This field is required for set. It is not allowed for other Harald requests. |
| QUALIFIERS: | This optional field contains a dictionary or hash of key/value pairs that provide parameters for the request. For example, the specific adapter to be operated on is identified by the ‘adapter_path‘ key. This might appear as ‘:adapter_path => "/org/bluez/hci0"‘ in a ‘show "address"‘ request (see Listing 6 and Example of Qualifier Field below). The qualifier hash field also contains entries for labels and ids to be associated with the request. |
| PREDICATE: | Harald defines predicates such as should_be, should_not_be and should_match. These predicates may be used with the block parameter (shown as result in Listing 5) returned as the Harald request is completed. |
Action Verbs
Each Harald action verb — show, list, set, and monitor — operates on a property. Additional verbs are defined for Bluetooth devices, for example headphones and keyboards. Bluetooth devices connect to adapters. The additional verbs for devices include create_device, remove_device, and discover_services.
show
The following simple example of a show request instructs Harald to fetch the address of the Bluetooth adapter located at path “/org/bluez/hci0″. The address is displayed by printing out the value of the code block’s result parameter.
Listing 6: Show Adapter Address
show "address", :adapter_path => "/org/bluez/hci0" do |result| puts "The address of my adapter is #{result}" end
The output from Listing 6 is:
The address of my adapter is 00:1F:3A:F2:52:C7
For adapters, the properties allowed for show are:
Listing 7: Adapter Properties
address
discoverable
discoverabletimeout
discovering
mode
name
pairable
pairabletimeout
periodicdiscovery
powered
requestmode
Each property may be prefixed with ‘adapter_‘ (the default) to indicate that show should be applied to the adapter class of components. A property may be specified as a symbol or quoted string.
For devices, the properties allowed include:
Listing 8: Device Properties
adapter
address
alias
class
icon
name
paired
trusted
uuids
Any device property, such as address, may contain the prefix ‘device_‘ to force Harald to apply the property to the device class of components. In this case, the property address would be written with an explicit prefix as ‘device_address‘. Otherwise, Harald depends upon implicit rules, such as the appearance of a qualifier like ‘:device_address‘ or ‘:device_path‘ in the qualifier hash (see below) to determine the target of the test case. Again, the property address by itself, without a prefix and without a device related key in the qualifier hash, would implicitly be associated by Harald with the system’s default adapter and not a device.
A hash of qualifiers (as shown in the syntax diagram of Listing 5) follows the property field in the show request syntax (but follows the value field in a set request). For some properties the provision of a hash is optional. Generally, qualifiers determine which adapter or device is being targeted as well as supply additional items, such as a label to be printed when the associated test case is executed.
An example hash of qualifiers is:
Listing 9: Example of Qualifier Field
{ :device_address=>"00:1A:45:6A:8B:4B", :label=>"\nTESTING some property: " }
Valid qualifier keys include ‘:device_address‘, ‘:adapter_address‘, ‘:id‘ and ‘:label‘. The qualifier ‘:id‘ optionally identifies a test case (e.g., :id=>"change adapter alias") and can be used to select a test case to run. In addition, for some requests such as ‘list :adapter‘ or ‘show :device_address‘, the qualifier key ‘:adapter_path‘ or ‘:device_path‘ may be given.
The final part of the Harald request is a standard code block identified by the following Ruby syntax:
Listing 10: Request Results Block
do |result| ... end
where ‘result‘ is a Harald Result object that holds the value of the property retrieved (or modified, in the case of set) by the Harald request. The Result object can be printed out or it can be saved in another variable. More commonly, however, it is used in a predicate that constructs an assertion about the expected value of a test case. Three predicates are allowed:
Listing 11: Predicate Methods
RESULT_OBJECT.should_be <some_expected_value | RESULT_OBJECT> RESULT_OBJECT.should_match <expected_text_pattern> or RESULT_OBJECT.should_not_be <some_forbidden_value | RESULT_OBJECT>
These predicates are instance methods of the Result class that evaluate the truth of an assertion. They also increment internal pass and fail counters that can be reported at any time by issuing ‘report :passed‘ or ‘report :failed‘ requests. The counters are initialized or reset with a ‘reset :tests‘ request.
An important and very useful feature of Harald’s Result objects is that they also cascade. That is, most methods understood by the underlying value object contained within a Result object will automatically produce (through the magic of Ruby’s ‘missing_method‘ metaprogramming) a new Result object which can then be tested by the built-in predicate methods. This is illustrated by an example in the monitor section later in the article.
list
The list request is used to retrieve objects from the Bluetooth implementation. For example the following request will enumerate the Bluetooth adapters installed on your system:
Listing 12: List Bluetooth Adapters
list :adapters, :label=>"my adapters are: \n" do |result| pp result end
The output from this example is:
my adapters are: ["/org/bluez/hci0"]
The Harald list request accepts the following targets (Bluetooth objects identified in the property field):
Listing 13: List Targets
adapter
adapter_properties
adapters
bus
devices
manager
These targets are not true properties. Instead, they are treated simply as qualifiers for the list request. They are used as follows:
list :adapterandlist :busreturn a BlueZ Adapter object and D-Bus Bus object respectively.list :adapter_propertiesreturns a Hash of properties associated with a specified (or the default) Bluetooth adapter.list :adaptersreturns an array of adapter devices currently recognized by the Bluetooth system.list :devicesreturns an array of device paths currently recognized. Normally these device paths are created during pairing requests, but they also can be created by issuing an appropriate D-Bus message via the Haraldcreate_devicerequest.list :managerreturns a BlueZ Manager object (which can be examined with pp, for example).
The list request accepts ‘:label=>"some description"‘ or ‘:id=>"some test case id"‘ in the hash qualifier following the property field. Other qualifier keys are ignored.
set
The set request provides an interface for updating adapter and device properties. Some properties are read-only, like ‘address‘ and ‘discovering‘. However, most properties can be modified by set.
In the following example of set, the current value of the mode property of the default Bluetooth adapter is first displayed in a show request block. Then the mode is turned off and the new mode value is confirmed. Finally the original mode is reset and re-displayed.
Listing 14: Show, Set and Reset Mode of Default Adapter
show :mode do |original_mode| puts "my original mode is #{original_mode}" set :mode, "off" do |new_mode| new_mode.should_be "off" end set :mode, original_mode do |reset_mode| puts "my mode is again #{reset_mode}" end end
The output generated by this example is:
my original mode is connectable OK, got expected value "off" my mode is again connectable
Set operates on most of the same properties of adapters and devices as the show request. Set takes the value given by its third argument and updates its second argument, the specified property, accordingly. The type of the value can be a string, fixnum, or boolean, as required by the native format of the particular BlueZ property that is being modified. The value will be converted to an appropriate type if there is a mismatch. Note, however, that the given representation of the value must be convertible to the native format. For example, the value for property DiscoverableTimeout can be specified either as “123″ or 123. If specified as the string “123″, it will be converted to fixnum for conveyance through D-Bus to BlueZ. As another example, the device property trusted can be specified as true or false (TrueClass or FalseClass respectively). A target value for the property can also be given as the strings “true”, “on” or “yes” each of which will be converted to true. Any other value will be treated as false.
After the set property modification request has been issued, a result is returned as the parameter of the procedure block attached to the set request. The result will reflect the new value of the property as reported by the underlying Bluetooth system. Note that this value may be different than what was given to the set request as a value parameter. For example, an adapter’s mode can be set to connectable by giving the value ‘on’ or ‘connectable’ as the third argument to the set request. But the result will be reported by the BlueZ implementation as the string ‘connectable’ in any case. A good way to see what properties and values your adapter actually provides, as identified by Bluetooth, is to issue a list :adapter_properties request.
monitor
Monitor is perhaps the simplest of all of the Harald requests, as the following example amply demonstrates:
Listing 15: Monitor Request
monitor
There are no parameters and no code block required. All that is needed is to enter the request verb monitor. Of course, the hash qualifiers ‘:id‘ and ‘:label‘ can be optionally provided to allow the request to be selected from the command line or to print out a descriptive message when the request is executed. This request produces an output stream that consists of a continuous flow of messages from the Bluetooth system. The kinds of messages that monitor has registered to receive from the BlueZ implementation are printed out as a banner at the beginning of the monitoring session. They include the signals PropertyChanged, DeviceFound, DeviceDisappeared, DeviceCreated and DeviceRemoved. As devices are paired or removed, or other properties are modified, time-stamped notifications are printed by monitor. A typical session might look like this:
2009-01-20 00:31:10 Setting signals [PropertyChanged, DeviceFound, DeviceDisappeared, DeviceCreated,
DeviceRemoved] for /org/bluez/hci0
2009-01-20 00:31:31 Signal: DeviceRemoved, interface: org.bluez.Adapter, adapter: /org/bluez/hci0,
device: /org/bluez/hci0/dev_00_1A_45_6A_8B_4B
2009-01-20 00:31:31 Signal: PropertyChanged, interface: org.bluez.Adapter, adapter: /org/bluez/hci0,
property 'Devices' changed to ''
2009-01-20 00:36:54 Signal: PropertyChanged, interface: org.bluez.Adapter, adapter: /org/bluez/hci0,
property 'Discovering' changed to 'true'
2009-01-20 00:36:56 Signal: DeviceFound, [ 00:14:51:D3:3C:5B ], properties: RSSI = -48
Name = remy's mouse
Class = 0x002580
Alias = remy’s mouse
Icon = input-mouse
Address = 00:14:51:D3:3C:5B
2009-01-20 00:37:00 Signal: PropertyChanged, interface: org.bluez.Adapter, adapter: /org/bluez/hci0,
property 'Discovering' changed to 'false'
2009-01-20 00:37:03 Signal: DeviceCreated, interface: org.bluez.Adapter, adapter: /org/bluez/hci0,
device: /org/bluez/hci0/dev_00_14_51_D3_3C_5B
2009-01-20 00:37:03 Signal: PropertyChanged, interface: org.bluez.Adapter, adapter: /org/bluez/hci0,
property 'Devices' changed to '/org/bluez/hci0/dev_00_14_51_D3_3C_5B'
2009-01-20 00:37:36 Signal: PropertyChanged, interface: org.bluez.Adapter, adapter: /org/bluez/hci0,
property 'Devices' changed to '/org/bluez/hci0/dev_00_14_51_D3_3C_5B'
.
.
.
A monitor request should appear as the final item in your Harald script because it is designed to produce a continuous stream of notifications and does not return control to the main script once started. If test cases have previously been executed, a ‘report :passed‘ or ‘report :failed‘ request should be placed in the script before calling monitor. The following snippet is an example of this strategy:
Listing 16: Mixing Monitor with Other Actions
reset :tests, :id=>"grp1" list :adapters, :label=>"my two adapters are: \n", :id=>"grp1_adapters" do |result| pp result result.length.should_be 2 end show "address", :adapter_path => "/org/bluez/hci1", :id=>"grp1_address" do |result| puts "The address of my second adapter is #{result}" result.should_not_be nil end report :passed, :id=>"grp1" monitor :id=>"grp2"
The output from this script is:
my two adapters are: ["/org/bluez/hci0", "/org/bluez/hci1"] OK, got expected value "2" The address of my second adapter is 00:0A:3A:6F:99:1D OK, did not get unallowed value "" Passed 2 of 2 tests 2009-01-20 11:32:30 Setting signals [PropertyChanged, DeviceFound, DeviceDisappeared, DeviceCreated, DeviceRemoved] for /org/bluez/hci0 ^C 2009-01-20 11:32:31 Caught interrupt signal, exiting
In this example, monitor will normally be executed after the two previous test cases have completed and a report of their success or failure is displayed. However, if this script is invoked from the command line with the pattern selection option -p grp2, then only the monitor request will be run. More complex selection patterns can be given, for example -p grp[12] to run both groups of test cases. This is equivalent to writing -p 'grp1|grp2' as well as to the shorter but less descriptive alternative of providing no option for pattern selection at all. Also notice that the should_be predicate of the first test case (’grp1_adapters‘) is preceded by a method call to length. A neat feature of the Result class is that any Ruby method (to which the underlying value of the result object responds) can be applied to the result object and then the return value can be tested with the built-in predicates in the normal way.
create_device
Create_device is used to send a CreateDevice request to the Bluetooth implementation. CreateDevice generates a DBus device path from the device’s MAC address.
In the following example, create_device causes Harald to send a BlueZ CreateDevice message to the system’s default Bluetooth adapter. The result contains a newly minted value for the path of the device.
Listing 17: Create Device
daddr = "00:1A:45:6A:8B:4B" create_device daddr, :label=>"creating device at #{daddr}\n" do |result| pp result end
The output from this request is the following.
creating device at 00:1A:45:6A:8B:4B #<TestComponent::Result:0xb79ab6b0 @result=["/org/bluez/hci0/dev_00_1A_45_6A_8B_4B"]>
The create_device request accepts a device address (not path) as its first argument. Alternatively, the device address may be assigned to a ‘:device_address‘ key in the qualifier hash. The result parameter of the code block is a Result object that contains an array with the device path as its single element.
The BlueZ implementation provides additional device creation methods such as CreatePairedDevice and CancelDeviceCreation. These methods may be supported in a later version of Harald.
remove_device
In the next example, a list :devices request is used to find the first Bluetooth device attached to the default adapter. If a device is located, a remove_device request is issued which causes Harald to send a BlueZ RemoveDevice message to the Bluetooth adapter module requesting removal of the device with the specified path. The device is then re-created with create_device and success is tested with a should_be predicate.
Listing 18: Remove and Recreate First Device on Default Adapter
list :devices do |result| dpath = result.first unless dpath.nil? daddr = dpath.sub(/.*dev_/,'').gsub(/_/,':') remove_device dpath, :label=>"removing device at #{dpath}\n" create_device daddr, :label=>"creating device at #{daddr}\n" do |result| result.should_be dpath end end end
This script displays the following output:
removing device at /org/bluez/hci0/dev_00_1A_45_6A_8B_4B creating device at 00:1A:45:6A:8B:4B OK, got expected value "/org/bluez/hci0/dev_00_1A_45_6A_8B_4B"
The remove_device request accepts a device address or path as its first argument. Alternatively, a device address may be assigned to a ‘:device_address‘ key, or a device path may be assigned to a ‘:device_path‘ key, in the qualifier hash. Note that remove_device does not use a code block to post back from the system the results of the operation. In this version of Harald, if a code block is supplied, it is ignored.
discover_services
The discover_services request causes Harald to fetch a device’s service records that match a given UUID. In the following example, the device address is provided in the first parameter and the UUID is given in the second. The result is a hash with an integer key (a BlueZ record handle) and a corresponding XML document that identifies the services associated with the UUID.
Listing 18: Discover Services
discover_services "00:1A:45:6A:8B:4B", "00001108-0000-1000-8000-00805f9b34fb", :label=>"starting service discovery\n" do |result| pp result end
An equivalent request can be written with hash qualifier keys replacing the positional parameters.
Listing 19: Discover Services Using Alternative Syntax
discover_services(:device_address=>"00:1A:45:6A:8B:4B", :service_pattern=>"00001108-0000-1000-8000-00805f9b34fb", :label=>"starting service discovery\n" ) { |r| pp r }
The output of either flavor of discover_services is:
starting service discovery #<TestComponent::Result:0xb78acac0 @result= [{65537=>"<?xml version="1.0" encoding="UTF-8" ?> <record> <attribute id="0x0000"> <uint32 value="0x00010001" /> </attribute> <attribute id="0x0001"> <sequence> <uuid value="0x1108" /> <uuid value="0x1203" /> </sequence> </attribute> <attribute id="0x0004"> <sequence> <sequence> <uuid value="0x0100" /> </sequence> <sequence> <uuid value="0x0003" /> <uint8 value="0x02" /> </sequence> </sequence> </attribute> <attribute id="0x0006"> <sequence> <uint16 value="0x656e" /> <uint16 value="0x006a" /> <uint16 value="0x0100" /> </sequence> </attribute> <attribute id="0x0009"> <sequence> <sequence> <uuid value="0x1108" /> <uint16 value="0x0100" /> </sequence> </sequence> </attribute> <attribute id="0x0100"> <text value="Headset" /> </attribute> <attribute id="0x0302"> <boolean value="true" /> </attribute> </record> "}]>
How Testing Works
Action verbs reset and report and predicates should_be, should_not_be and should_match are the building blocks for automating test scenarios for the Bluetooth system under Linux. These expressions of the test framework specify assertions as well as enable you to summarize the results of arbitrary sequences of test cases.
Test reporting instructions include ‘reset :test‘, which resets the system’s pass/fail counters to 0, and ‘report :passed‘ which prints a simple report about how many tests or assertions have passed. Test assertions or predicates may be attached to the result parameter of the procedure block associated with a request. Each invocation of ‘should_be‘, ‘should_not_be‘ or ‘should_match‘ is an assertion that counts as a single test.
Advanced Examples
The following examples illustrate more sophisticated ways to use Harald’s DSL to help automate testing of the Linux Bluetooth implementation.
Listing 20: Changing, testing and reverting a device alias property
reset :tests daddr = "00:1A:45:6A:8B:4B" prop = :device_alias show prop, :device_address=>daddr, :label=>"\nTESTING #{prop.to_s.upcase}: " do |old_value| puts "my original #{prop}: \"#{old_value}\"" test_value = "alias-of-jabra-headset" set prop, test_value, :device_address=>daddr, :label=>"changing #{prop} to \"#{test_value}\"\n" do |new_value| new_value.should_be test_value end show :device_properties, :device_address=>daddr do |new_props| print "my device properties are now: "; pp new_props.result end set prop, old_value, :device_address=>daddr, :label=>"reverting #{prop} to \"#{old_value}\"\n" do |reset_value| reset_value.should_be old_value end end report :passed
Running this Harald script produces the following output:
TESTING DEVICE_ALIAS: my original device_alias: "Jabra BT125"
changing device_alias to "alias-of-jabra-headset"
OK, got expected value "alias-of-jabra-headset"
my device properties are now: {"Connected"=>false,
"UUIDs"=>
["00001108-0000-1000-8000-00805f9b34fb",
"0000111e-0000-1000-8000-00805f9b34fb"],
"Name"=>"Jabra BT125",
"Trusted"=>false,
"Class"=>2098180,
"Icon"=>"audio-card",
"Alias"=>"alias-of-jabra-headset",
"Adapter"=>"/org/bluez/hci0",
"Paired"=>true,
"Address"=>"00:1A:45:6A:8B:4B"}
reverting device_alias to "Jabra BT125"
OK, got expected value "Jabra BT125"
Passed 2 of 2 tests
In the next example, each legal value for every adapter property is tested and then scores for the number of tests passed and failed are reported at the end. This kind of test scenario is especially useful for regression testing. Note that some adapter property names have been deprecated in recent versions of BlueZ. These are identified as “unknown adapter properties” by Harald in the current version of BlueZ being tested. For example, the properties Pairable and PairableTimeout are now called Discoverable and DiscoverableTimeout. Test cases that use the new names for these properties work.
Listing 21: Automate production of test cases for standard adapter properties
reset :tests STANDARD_ADAPTER_PROPERTIES.each do |prop| show prop, :label=>"\nTESTING #{prop.to_s.upcase}: " do |old_value| puts "my original #{prop}: \"#{old_value}\"" STANDARD_ADAPTER_PROPERTY_SIGNATURES[prop.to_s].each do |test_value| next if test_value.nil? set prop, test_value, :label=>"changing #{prop} to \"#{test_value}\"\n" do |new_value| new_value.should_be test_value end end set prop, old_value, :label=>"restoring #{prop} to \"#{old_value}\"\n" do |reset_value| reset_value.should_be old_value end end end report :passed report :failed
This Harald script produces the following output:
TESTING PAIRABLE: Unknown adapter property: pairable TESTING NAME: my original name: "harald-0" changing name to "temp_name_0" OK, got expected value "temp_name_0" restoring name to "harald-0" OK, got expected value "harald-0" TESTING ADDRESS: my original address: "00:1F:3A:F2:52:C7" restoring address to "00:1F:3A:F2:52:C7" Cannot set address property of adapter /org/bluez/hci0 OK, got expected value "00:1F:3A:F2:52:C7" TESTING PAIRABLETIMEOUT: Unknown adapter property: pairabletimeout TESTING MODE: my original mode: "connectable" changing mode to "connectable" OK, got expected value "connectable" changing mode to "off" OK, got expected value "off" restoring mode to "connectable" OK, got expected value "connectable" TESTING DISCOVERABLE: my original discoverable: "false" changing discoverable to "on" OK, got expected value "on" changing discoverable to "off" OK, got expected value "off" restoring discoverable to "false" OK, got expected value "false" TESTING DISCOVERABLETIMEOUT: my original discoverabletimeout: "5" changing discoverabletimeout to "123" OK, got expected value "123" restoring discoverabletimeout to "5" OK, got expected value "5" TESTING POWERED: my original powered: "true" changing powered to "on" OK, got expected value "on" changing powered to "off" OK, got expected value "off" restoring powered to "true" OK, got expected value "true" TESTING DISCOVERING: my original discovering: "false" restoring discovering to "false" Cannot set discovering property of adapter /org/bluez/hci0 OK, got expected value "false" TESTING REQUESTMODE: Unknown adapter property: requestmode TESTING PERIODICDISCOVERY: Unknown adapter property: periodicdiscovery Passed 15 of 15 tests Failed 0 of 15 tests
In examining this listing, note that ‘RequestMode’ and ‘PeriodicDiscovery’, as well as ‘Pairable’ and ‘PairableTimeout’ mentioned previously, are deprecated properties in the latest BlueZ implementation. Also note that adapter properties ‘Address’ and ‘Discovering’ are read-only, a fact we are notified about in the listing above by the “Cannot set PROPERTY…” messages.
Installing Harald
A gem for the version of Harald described in this article can be downloaded at harald-0.1.0.gem. After downloading this package, run ‘gem install harald-0.1.0.gem‘ to deploy it to your local gem directory. To use Harald you will also need to install the Ruby dbus package available from trac.luon.ne. The latest Subversion package for Ruby dbus can be obtained via ‘svn co https://svn.luon.net/svn/ruby-dbus/trunk ruby-dbus‘.
Running Harald
Running Harald is simple. Just execute the Ruby file containing your Harald script with the standard ruby command:
ruby YOUR_HARALD_SCRIPT.rb
A request or test case selection pattern can be entered using the -p option following the Harald script file name.
ruby YOUR_HARALD_SCRIPT.rb -p selection_pattern
All requests whose ids match the given pattern will be executed. If no selection pattern is provided, all requests will be run. Note that if a selection pattern is provided, then requests that do not have ids will not be executed. That is, when a selection pattern is given, only those requests that have positive matching ids will be eligible to run. The request id is the optional ‘:id‘ element of the qualifier hash parameter attached to a request. Test scenarios or suites can be generated by providing a regular expression that matches shared id patterns.
Summary
Harald is already a useful DSL for automating a variety of component tests against the BlueZ implementation of Bluetooth on Linux. However, many features of BlueZ (like agents and services) are not yet addressed by Harald. Still, we hope that this initial, limited DSL can form the basis for building more comprehensive and powerful tools to test and monitor the Bluetooth world on Linux. We also hope that Harald will serve as an inspiration for developing other Ruby-based DSLs suitable for system and infrastructure interaction.


