1# EDTT framework and BabbleSim
2(With focus on Zephyr BLE testing on a PC)
3
4## Introduction
5
6The purpose of this document is to provide a basic understanding of the EDTT Framework for developers writing Tests for the Framework. It is assumed that the reader is familiar and knows how to use [BabbleSim](http://babblesim.github.io/), and how to run  [Zephyr](https://docs.zephyrproject.org/) code on a standard Linux PC thru the [nrf52_bsim board](https://docs.zephyrproject.org/latest/boards/posix/nrf52_bsim/doc/index.html).
7The EDTT Framework is made for testing BLE functionality in the Zephyr Bluetooth implementation.
8Here, the EDTT Framework is using the BabbleSim radio simulation, together with simulated Zephyr Bluetooth stacks, where the Zephyr code is running on a standard Linux PC rather than a dedicated embedded system.
9This setup provides the developer with a powerful environment, where tracing and debugging becomes a lot easier compared to an embedded system.
10Tests, and the EDTT framework are written in Python, and their execution is decoupled from the simulated Zephyr Bluetooth stacks, in the sense that tests are running in their own process communicating with the simulated Zephyr Bluetooth stack through Named Pipes.
11Each test relies on a specific Zephyr application running in the simulated devices. This application can be a standalone application reacting to stimuli received “over the air”, a dedicated application responding to specific commands sent from the EDTT or a combination of the two.
12One of these test applications responds to standard HCI commands sent from the test and returns standard Events to the test.
13
14## Framework
15The Framework consists of a test execution part and a set of simulated Zephyr Bluetooth stacks as shown in the Figure 1 (EDTT framework TODO) below.
16
17![Figure 1 EDTT Framework](Figure1_edtt_framework.svg "Figure 1 EDTT Framework")
18
19The two execution environments are connected thru Named Pipes. Since Bluetooth is all about communicating entities, a test needs access to more than a single DUT.
20
21The EDTT transport ensures that the simulation is halted when needed while the EDTT is processing. That is, the result of the simulation does not depend on the python execution speed.
22In principle any number of DUTs can be supported, but in reality only two DUTs are used for testing. The setup is illustrated in the next figure .
23
24![Figure 2 EDTT](Figure2_EDTT.svg "Figure 2 EDTT")
25
26In addition to the DUTs, the EDTT BabbleSim transport also has a low level device that connects directly to BabbleSim. This device can be used to send raw BLE packets without going through a Zephyr device (with the limitations that brings).
27
28---
29### The transport
30
31The transport is conceptually a two-way FIFO for each DUT.
32
33Commands are atomic transactions that maps to calls on the DUT side. Cannot be interleaved (for simplicity).
34
35Utils are a set of commands shared between tests.
36
37Transport EDTT side API:
38
39* open(device_id), connect(device_id), close(device_id)
40* send(device_id, message),
41* recv(device_id, count, timeout)
42* wait(time), get_time()
43
44Transport DUT side API:
45
46* start(), stop()
47* read(u8_t *pbuffer, size_t count, int flags)
48* write(u8_t *pbuffer, size_t count, int flags)
49
50---
51
52In BabbleSim, EDTT is just another program being executed in parallel with the programs running the individual DUTs. Each DUT is configured to run one of the EDTT Test APPs, implementing the Command executor.
53
54The EDTT transport must know how many DUTs it needs to connect to and the identity of each DUT. This information is passed as run-time parameters to the EDTT. A typical execution of BabbleSim and two DUTs could look as shown here:
55
56```
57./bs_2G4_phy_v1 –s=Test –D=3 –sim_length=5e6 -dump_imm &
58
59./bs_nrf52_bsim_hci_test_app_bsim –s=Test –d=1 –v=3 &
60
61./bs_nrf52_bsim_hci_test_app_bsim –s=Test –d=2 –v=3
62
63./bs_edttool –s=Test –d=0 –t bsim -T hci_verification –C HCI/CIN/BV-04-C -l -D=2 -devs 1 2 –RxWait=2.5e3 &
64```
65
66
67The radio simulation is configured with the simulation name “Test” (-s=Test). It is told to host three devices (-D=3). The simulation length is set to 5 seconds (-sim_length=5e6). And it is told to disable cached writes for the dump files (-dump_imm).
68
69The EDTT Test APP is started twice to simulate the two DUTs. The EDTT Test APP is handed the name of the simulation “Test” (-s=Test) and its own device identifier (-d=1 or –d=2).
70
71Last, the EDTT itself is started. It is handed the name of the simulation “Test” (-s=Test), its own device identifier (-d=0), the number of DUTs to service (-D=2) and the device identifiers for the two DUTs (-dev 1 2).
72
73To run with the low level device enabled, EDTT itself also needs to be assigned a BabbleSim device number. This is done via the --low-level-device-nbr argument. For example, expanding on the above:
74
75```
76./bs_2G4_phy_v1 –s=Test –D=4 –sim_length=5e6 -dump_imm &
77
78./bs_nrf52_bsim_hci_test_app_bsim –s=Test –d=1 –v=3 &
79
80./bs_nrf52_bsim_hci_test_app_bsim –s=Test –d=2 –v=3 &
81
82./bs_edttool –s=Test –d=0 –t bsim -T hci_verification –C HCI/CIN/BV-04-C -l --low-level-device-nbr=3 -D=2 -devs 1 2 –RxWait=2.5e3
83```
84
85Now the simulation environment is started and ready to receive HCI Command requests from a test. See Tests to see how the test execution is started.
86
87Details on the transport layer API overall can be found in docs/EDTT_trasnport.md
88
89More information about the bsim transport, both the embedded driver and python side can be found in docs/EDTT_transport_bsim.md
90
91
92## HCI Command API
93
94One of the EDTT test applications running in the simulated Zephyr Bluetooth environment implements an HCI Command API that enables tests to execute HCI commands and receive the results of these executions as well as the HCI events generated. The HCI Command API is implemented as a request, reply protocol. In order to use this API, tests must pack commands into a byte stream that is sent over  to the EDTT test application. The EDTT test application will generate a reply that is sent back to the test.
95All requests and replies share a common syntax of the form:
96```
97	<request> ::= <request_id> <request_size>  [ <request_parameter_list> ]
98	<request_id> ::= 1 | 3 | 5 | … | 193
99	<request_size> ::= 0 | 1 | 2 | … | 255
100	<request_parameter_list> ::= <request_parameter> { <request_parameter> }
101	<request_parameter> ::= 0 | 1 | 2 | … | 255
102	<reply> ::= <reply_id> <reply_size> [ <reply_parameter_list> ]
103	<reply_id> ::= 2 | 4 | 6 | … | 194
104	<reply_size> ::= 0 | 1 | 2 | … | 255
105	<reply_parameter_list> ::= <reply_parameter> { <reply_parameter> }
106	<reply_parameter> ::= 0 | 1 | 2 | … | 255
107
108<request_id>, <request_size>, <reply_id> and <reply_size> are little-endian 16 bit unsigned numbers. <request_parameter> and <reply_parameter> are 8 bit numbers.
109```
110
111## Packet inspection
112
113It is possible to inspect the raw packets sent by the upper and lower tester via BabbleSims dump files. Note that you will usuallly want to use the `-dump_imm` command line argument when using packet inspection; Otherwise the dump files will use cached writes and the latest packets will likely not show up in EDTT.
114
115The raw packets will get decoded into a `Packet` before being handed over to the test cases via the `Packets` class. The content of a `Packet` is:
116
117* `direction`: Either `'Tx'` or `'Rx'`. Note that the current implementation only reads the Tx part of the dump files, so this will currently always be `'Tx'`
118* `idx`: The BSim device id of the device that received or transmitted the package (see `direction` for whether it was a transmit or receive). Will correspond to lower or upper tester
119* `ts`: The start timestamp of the packet (in microseconds)
120* `aa`: The access address used
121* `channel_num`: The channel number used (note: Not the channel *index*, but the channel *number* - ie. advertising channels are 0, 12 and 39)
122* `phy`: The BLE PHY used - one of `'1M'` or `'2M'`
123* `data`: The raw data of the packet (excluding header)
124* `type`: The type of packet, for example `'ADV_IND'` or `'CONNECT_IND'`
125* `header`: The header of the packet - a named tuple containing `pdu_type`, `ch_sel`, `tx_add`, `rx_add` and `payload_len`
126* `payload`: The decoded payload of the packet. Content depends on the type of packet; It will generally be a named tuple containing the fields specified in the BT Core Spec. One notable exception is for the extended advertisement packets - for these the payload is a `dict` since the fields may or may not be present
127
128### Packets class interface
129
130There are a few different ways to inquire about packets sent on the phy from within a test case. Generally, the way to use the API is to filter for the packets you are interested - see the API describtion below.
131
132#### Packets.fetch(packet_filter=())
133
134Returns an iterator to use in a `for` loop. Will iterate through all packets matching the provided filter in the order of oldest packet to newest.
135
136Arguments:
137
138* `packet_filter`: Type or list of types to match on - for instance `('AUX_CONNECT_RSP', 'AUX_CONNECT_REQ')`
139
140Simple example - to loop through all AUX_ADV_IND packets and print them:
141
142```
143    for packet in packets.fetch(packet_filter=('AUX_ADV_IND')):
144        # Do something with packet
145        print(packet)
146```
147
148#### Packets.find(packet_type=None)
149
150Returns the first (ie. oldest) `Packet` matching the provided type filter.
151
152Arguments:
153
154* `packet_type`: Type or list of types to match on - for instance `('AUX_CONNECT_RSP', 'AUX_CONNECT_REQ')`
155
156#### Packets.findLast(packet_filter=())
157
158Returns the last (ie. newest) `Packet` matching the provided filter.
159
160Arguments:
161
162* `packet_filter`: Type or list of types to match on - for instance `('AUX_CONNECT_RSP', 'AUX_CONNECT_REQ')`
163
164#### Packets.flush()
165
166Flushes the current packets to start fresh. All packets currently known will be removed and can no longer be retrieved.
167Note that this is implicitly done between test cases, so calling this function is only needed if a flush inside a test case is wanted.
168
169## Tests
170
171Tests are written in Python and executed in their own execution environment. All the necessary support for handling any LE HCI command is implemented in the EDTT Test APP and the supporting Python module basic_commands.py. The Python module basic_commands.py provides a function interface for all supported LE HCI commands. The supporting Python module utils.py provides a set of common functions that can be utilized by tests.
172
173### Writing tests
174
175Tests are grouped in modules.
176A test module must implement the functions `get_tests_specs()` and `run_a_test()`:
177
178* `get_tests_specs()`: Must return a dictionary, containing one entry for each test provided by the module. Each entry of type TestSpec TestSpec is defined in the supporting Python module test_spec.py from components, which includes between others the test name, description, and the number_devices parameter, which specifies how many devices this test or tests requires. See the example below.
179* `run_a_test()`: Which will execute an actual test given a TestSpec.
180
181Python tests that wish to exploit the HCI command API should start by importing the two Python modules basic_commands.py and utils.py from components.
182
183```
184from components.utils import *;
185from components.basic_commands import *;
186```
187
188This provides access to all the LE HCI commands in basic_commands.py and the utility functions in utils.py.
189
190For the actual tests, call functions from basic_commands.py and handle the HCI events generated. It is essential that the generated HCI events are handled in the test, otherwise they will remain in the event queue and possibly be picked up by later tests, which will then fail.
191
192Please note that the call to get_event is blocking, it will wait for an event to arrive in the queue. If unsure whether an event is generated in a particular situation, use the has_event function to poll for events prior to calling get_event.
193
194In the example below, the test is calling the HCI function read_local_version_information with the parameters transport, 0 and 100. Transport is a reference to the transport layer, to be used for sending the HCI command to the DUT. The 0 is an identifier for the DUT to receive the command. The 100 is a timeout parameter (specified in milliseconds) specifying how long to wait for an answer to arrive.
195
196The read_local_version_information command will return a status and several other parameters. Here we only pick up the status and check that it is zero, indicating that the command succeeded.
197
198The read_local_version_information command will generate a COMMAND COMPLETE event, which is picked up. The test verifies that the event generated is a COMMAND COMPLETE event and displays details from the event using one of the utility functions from utils.py.
199
200Note that get_event returns an eventTime, which is derived from the system up-time in milliseconds.
201
202```
203"""
204   HCI/CIN/BV-04-C [Read Local Version Information Command]
205"""
206def main(args, transport, trace):
207    trace.trace(3, \
208          "HCI/CIN/BV-04-C [Read Local Version Information Command]");
209    try:
210        status = read_local_version_information(transport,0,100)[0];
211        trace.trace(1, "VIC returns status: 0x%02X" % status);
212        success = status == 0;
213        eventTime,event,subEvent,eventData = get_event(transport,0,100);
214        success = success and (event == Events.BT_HCI_EVT_CMD_COMPLETE);
215        showEvent(event, eventData);
216    except Exception as e:
217        trace.trace(3, "VIC test failed: %s" % str(e));
218        success = False;
219
220    trace.trace(3, "VIC test " + ("PASSED" if success else "FAILED"));
221```
222
223For a trivially simple test and module example check tests/echo_test.py
224
225### Executing tests
226
227A Python wrapper has been made to make test execution easy. The wrapper is edttool.py. The syntax is shown below.
228
229The `<test>` argument is the name of the Python file (without .py) holding the code for the test to execute. The `<trace-level>` argument is a number that can be used to limit the trace output from the test.
230
231When the bsim transport is selected: The `<sim_id>` is a string that must match the `<sim_id>` used for the execution of the EDTT APP in the simulator. The `<eddtool_dev_nbr>` is the device identifier assigned to the EDTT itself.
232
233```
234edttool.py [-h] [-v trace-level] -t TRANSPORT -T TEST [-C CASE] [--shuffle] [-S] [--seed SEED] [-s sim_id] [-d eddtool_dev_nbr] [-l] [--low-level-device-nbr low_level_dev_nbr]
235
236
237Example:
238edttool.py –s=Test –d=0 –t bsim -T hci_verification –C HCI/CIN/BV-04-C
239```
240
241## Repositories
242
243BabbleSim, EDTT Tool and EDTT applications all resides in different GIT repositories:
244
245* Babblesim resides in: https://github.com/BabbleSim
246
247* nrf52_bsim is part of Zephyr upstream repo: https://github.com/zephyrproject-rtos/zephyr
248
249* The Zephyr edtt_app is also part of upstream Zephyr
250
251* EDTTool resides in the repository: https://github.com/EDTTool/EDTT
252