Import Mbed OS hard-float snapshot
This commit is contained in:
14
TEST_APPS/testcases/README.md
Normal file
14
TEST_APPS/testcases/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
Icetea tests
|
||||
============
|
||||
|
||||
This folder contains all the test cases done with Icetea residing in `mbed-os`.
|
||||
The tests are divided in to subfolders and each subfolder contains a set of testcases.
|
||||
The subfolder has a description of all the testcases it contains.
|
||||
|
||||
Testcases
|
||||
---------
|
||||
|
||||
Current testcases:
|
||||
|
||||
- [`example`](https://github.com/ARMmbed/mbed-os/blob/master/TEST_APPS/testcases/example)
|
||||
- [`nanostack_mac_tester`](https://github.com/ARMmbed/mbed-os/blob/master/TEST_APPS/testcases/nanostack_mac_tester)
|
||||
16
TEST_APPS/testcases/__init__.py
Normal file
16
TEST_APPS/testcases/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Copyright (c) 2020 Arm Limited
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
22
TEST_APPS/testcases/example/README.md
Normal file
22
TEST_APPS/testcases/example/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
##Example tests
|
||||
|
||||
This folder contains example tests for Icetea
|
||||
The test located under this folder is dependent of the application [`exampleapp`](https://github.com/ARMmbed/mbed-os/blob/master/TEST_APPS/device/exampleapp)
|
||||
The exampleapp is disabled by default, to be able to run the test_cmdline with the exampleapp, either remove the preprocessor macro from exampleapp.cpp or add `-DICETEA_EXAMPLE_ENABLED` to the mbed test command
|
||||
|
||||
## Test cases
|
||||
|
||||
### `test_cmdline`
|
||||
|
||||
**Description:**
|
||||
Send command line commands to target over serial interface.
|
||||
This test introduces the Icetea test case structure to the user.
|
||||
|
||||
**Test steps:**
|
||||
Send "echo hello world" to the target.
|
||||
Target sends "hello world" back to the host machine.
|
||||
Send help to the target.
|
||||
Target prints out the command line commands the application supports.
|
||||
|
||||
**Expected result:**
|
||||
The test exits without timeouts.
|
||||
16
TEST_APPS/testcases/example/__init__.py
Normal file
16
TEST_APPS/testcases/example/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Copyright (c) 2020 Arm Limited
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
52
TEST_APPS/testcases/example/test_cmdline.py
Normal file
52
TEST_APPS/testcases/example/test_cmdline.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Copyright 2018 ARM Limited
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from icetea_lib.bench import Bench
|
||||
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self,
|
||||
name="test_cmdline",
|
||||
title="Smoke test for command line interface",
|
||||
status="released",
|
||||
purpose="Verify Command Line Interface",
|
||||
component=["cmdline"],
|
||||
type="smoke",
|
||||
requirements={
|
||||
"duts": {
|
||||
'*': {
|
||||
"count": 1,
|
||||
"type": "hardware",
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-exampleapp"
|
||||
}
|
||||
},
|
||||
"1": {"nick": "dut1"},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
|
||||
def case(self):
|
||||
self.command("dut1", "echo hello world")
|
||||
self.command("dut1", "help")
|
||||
|
||||
def teardown(self):
|
||||
pass
|
||||
139
TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py
Normal file
139
TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import threading
|
||||
import os,sys
|
||||
from icetea_lib.bench import Bench
|
||||
from icetea_lib.TestStepError import TestStepFail
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self, name = "ED_scan",
|
||||
title = "ED scan test",
|
||||
status = "released",
|
||||
type = "smoke",
|
||||
subtype = "",
|
||||
execution = {
|
||||
"skip": {
|
||||
"value": False,
|
||||
"reason": ""
|
||||
}
|
||||
},
|
||||
author = "Valtteri Erkkila",
|
||||
purpose = "Tests reading the ED values from channels 11-16",
|
||||
feature = ["MLME-SCAN (ED)"],
|
||||
component = ["MAC"],
|
||||
requirements = {
|
||||
"duts": {
|
||||
'*': {
|
||||
"count":3,
|
||||
"type": "hardware",
|
||||
"allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW41Z"],
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-nanostack_mac_tester"
|
||||
}
|
||||
},
|
||||
"1":{"nick": "First"},
|
||||
"2":{"nick": "Second"},
|
||||
"3":{"nick": "Third"}
|
||||
}}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.channel = 11
|
||||
|
||||
def spam_channel(self, event):
|
||||
while not event.wait(0.1):
|
||||
self.lock_th.acquire()
|
||||
self.command("First", "data --dst_addr 01:02:03:00:00:00:00:03 --msdu {} --msdu_length {} --wait_for_confirm false".format(self.payload, len(self.payload)))
|
||||
self.command("Third", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu {} --msdu_length {} --wait_for_confirm false".format(self.payload, len(self.payload)))
|
||||
self.lock_th.release()
|
||||
|
||||
def mask_from_channel_list(self, channels):
|
||||
res = 0
|
||||
for ch in channels:
|
||||
res = res | ( 1 << ch)
|
||||
return hex(res)
|
||||
|
||||
def do_test_iteration(self):
|
||||
self.lock_th = threading.Lock()
|
||||
self.command("First", "mlme-reset")
|
||||
self.command("Second", "mlme-reset")
|
||||
self.command("Third", "mlme-reset")
|
||||
|
||||
self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01")
|
||||
self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02")
|
||||
self.command("Third", "addr --64-bit 01:02:03:00:00:00:00:03")
|
||||
|
||||
self.payload = "01234567890123456789012345678901234567890123456789"
|
||||
|
||||
# Start PAN coordinator
|
||||
self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel))
|
||||
# Start PAN beacon
|
||||
self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel))
|
||||
self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel))
|
||||
|
||||
#No reason to print their spamming
|
||||
self.command("First", "silent-mode on")
|
||||
self.command("Third", "silent-mode on")
|
||||
|
||||
self.stop_event = threading.Event()
|
||||
self.th = threading.Thread(target=self.spam_channel, args=(self.stop_event,))
|
||||
self.th.start()
|
||||
self.stopped = True
|
||||
channels = range(11,27)
|
||||
for i in range(0, 3):
|
||||
self.lock_th.acquire()
|
||||
# Reset MAC settings
|
||||
self.command("First", "mlme-reset")
|
||||
# Start PAN coordinator
|
||||
self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel))
|
||||
self.command("Third", "mlme-reset")
|
||||
# Start PAN beacon
|
||||
self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel))
|
||||
self.lock_th.release()
|
||||
# Scan all channels
|
||||
self.command("Second", "scan --scan_type 0 --scan_duration 7 --channel_mask {}".format(self.mask_from_channel_list(channels)))
|
||||
# Energy detection analysis
|
||||
self.command("Second", "analyze-ed --channel {} --above 100".format(self.channel))
|
||||
|
||||
def case(self):
|
||||
# Try tests few times because of potential RF failures
|
||||
loop = 0
|
||||
while loop < 5:
|
||||
try:
|
||||
self.do_test_iteration()
|
||||
break
|
||||
except TestStepFail:
|
||||
self.logger.info("Warning, iteration failed #" + str(loop+1))
|
||||
loop = loop + 1
|
||||
if (loop < 5):
|
||||
self.stop_event.set()
|
||||
self.th.join()
|
||||
self.delay(5)
|
||||
|
||||
else:
|
||||
raise TestStepFail("Too many failed iterations!")
|
||||
|
||||
def tearDown(self):
|
||||
self.command("First", "silent-mode off")
|
||||
self.command("Third", "silent-mode off")
|
||||
self.stop_event.set()
|
||||
self.th.join()
|
||||
del self.th
|
||||
self.reset_dut()
|
||||
|
||||
298
TEST_APPS/testcases/nanostack_mac_tester/README.md
Normal file
298
TEST_APPS/testcases/nanostack_mac_tester/README.md
Normal file
@@ -0,0 +1,298 @@
|
||||
## Nanostack MAC RF tests
|
||||
|
||||
This folder contains nanostack MAC RF tests for Icetea.
|
||||
The test located under this folder is dependent of the application [`nanostack_mac_tester`](https://github.com/ARMmbed/mbed-os/blob/master/TEST_APPS/device/nanostack_mac_tester).
|
||||
The nanostack MAC tester is disabled by default. To run the test cases with the application, either remove the preprocessor macro from main.cpp or add `-DICETEA_MAC_TESTER_ENABLED` to the `mbed test` command.
|
||||
|
||||
Icetea test cases are processed by passing commands through the `mbed-client-cli` command line. It is possible to manually replicate most test cases by following the instructions below.
|
||||
|
||||
In test cases with more than one device under test (DUT) the target device is given in the instructions as DUT1, DUT2 or DUT3.
|
||||
|
||||
## Test cases
|
||||
|
||||
### `address_read_and_write`
|
||||
|
||||
**Description:**
|
||||
|
||||
Write addresses to MAC interface, verify successful write.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
None
|
||||
|
||||
**Test steps:**
|
||||
|
||||
1. Set 64-bit MAC address to 01:02:03:00:00:00:00:01:
|
||||
`addr --64-bit 01:02:03:00:00:00:00:01`
|
||||
2. Set 16-bit MAC address to 0xABCD:
|
||||
`addr --16-bit 0xABCD`
|
||||
3. Set MAC PAN ID to 0xCDCD:
|
||||
`mlme-set --attr 0x50 --value_bytes CD:CD --value_size 2`
|
||||
4. Read and verify 64-bit address:
|
||||
`addr`
|
||||
|
||||
**Expected result:**
|
||||
|
||||
The address that was read is the same as the one that was written.
|
||||
|
||||
The test exits with status `PASS` without timeouts.
|
||||
|
||||
### `create_and_join_PAN`
|
||||
|
||||
**Description:**
|
||||
|
||||
Create two Personal Area Networks (PAN) and verify beacon transmission.
|
||||
|
||||
Requires 3 devices.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
1. All devices have 802.15.4 tranceivers and are in sufficient proximity.
|
||||
2. RF channel and adjacent channel are sufficiently clear.
|
||||
The RF channel is set in the test case method setUp(). The adjacent
|
||||
channel is automatically the next higher one, except for channel 26,
|
||||
where the adjacent channel becomes the next lower one. This is because
|
||||
26 is the highest available of the 16 channels in the 2.4-GHz band.
|
||||
|
||||
**Test steps:**
|
||||
|
||||
1. DUT1: Set MAC beacon payload to "mac-tester":
|
||||
`mlme-set --attr 0x45 --value_ascii mac-tester --value_size 10`
|
||||
2. DUT1: Set payload length:
|
||||
`mlme-set --attr 0x46 --value_uint8 10 --value_size 1`
|
||||
3. DUT2: Set MAC beacon payload to "second-mac-tester":
|
||||
`mlme-set --attr 0x45 --value_ascii second-mac-tester --value_size 17`
|
||||
4. DUT2: Set payload length:
|
||||
`mlme-set --attr 0x46 --value_uint8 17 --value_size 1`
|
||||
5. DUT1: Start PAN coordinator in chosen channel:
|
||||
`start --pan_coordinator true --logical_channel <channel 1>`
|
||||
6. DUT2: Start PAN coordinator in adjacent channel:
|
||||
`start --pan_coordinator true --logical_channel <channel 2>`
|
||||
7. DUT3: Scan with channel mask:
|
||||
`scan --channel_mask <hex channel mask>`
|
||||
Channel mask is given as a left bitshifted channel number in hexadecimal format.
|
||||
For channels 11 and 12, the channel mask becomes 0x1800.
|
||||
8. DUT3: Search beacon data for DUT1's beacon:
|
||||
`find-beacon --data mac-tester`
|
||||
9. DUT3: Search beacon data for DUT2's beacon:
|
||||
`find-beacon --data second-mac-tester`
|
||||
|
||||
**Expected result:**
|
||||
|
||||
DUT1 and DUT2 start beacon transmissions on respective channels.
|
||||
|
||||
DUT3 receives and parses beacon transmissions.
|
||||
|
||||
The test exits with status `PASS` without timeouts.
|
||||
|
||||
### `ED_scan`
|
||||
|
||||
**Description:**
|
||||
|
||||
Perform Energy Detection (ED) scan to find Personal Area Networks (PAN).
|
||||
|
||||
The test case requires that a lot of data is sent on the RF channel. In the test case
|
||||
this is done automatically at a rate that is hard to perform manually, which makes
|
||||
it difficult to run the test case manually.
|
||||
|
||||
Requires 3 devices.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
1. All devices have 802.15.4 tranceivers and are in sufficient proximity.
|
||||
2. RF channel is sufficiently clear.
|
||||
|
||||
**Test steps:**
|
||||
|
||||
1. Set addresses for devices:
|
||||
DUT1: `addr --64-bit 01:02:03:00:00:00:00:01`
|
||||
DUT2: `addr --64-bit 01:02:03:00:00:00:00:02`
|
||||
DUT3: `addr --64-bit 01:02:03:00:00:00:00:03`
|
||||
2. DUT1: Start PAN coordinator:
|
||||
`start --pan_coordinator true --logical_channel <channel>`
|
||||
3. DUT2,DUT3: Start PAN beacon:
|
||||
`start --pan_coordinator false --logical_channel <channel>`
|
||||
4. (optional) Silence beacon traces:
|
||||
`silent-mode on`
|
||||
5. Start repeatedly sending data between DUT1 and DUT3:
|
||||
DUT1: `data --dst_addr 01:02:03:00:00:00:00:03 --msdu <payload> --msdu_length <payload_length> --wait_for_confirm false`
|
||||
DUT3: `data --dst_addr 01:02:03:00:00:00:00:01 --msdu <payload> --msdu_length <payload_length> --wait_for_confirm false`
|
||||
In the test case this repeats every 100ms.
|
||||
6. DUT1,DUT3: Reset MAC settings:
|
||||
`mlme-reset`
|
||||
7. DUT1: Start PAN coordinator:
|
||||
`start --pan_coordinator true --logical_channel <channel>`
|
||||
8. DUT3: Start PAN beacon:
|
||||
`start --pan_coordinator false --logical_channel <channel>`
|
||||
9. DUT2: Scan channels 11-26:
|
||||
`scan --scan_type 0 --scan_duration 7 --channel_mask 0x7fff800`
|
||||
Channel mask is given as a left bitshifted channel numbers in hexadecimal format.
|
||||
10. DUT2: Do Energy Detection analysis on data:
|
||||
`analyze-ed --channel <channel> --above 100`
|
||||
|
||||
Test case repeats steps 5 to 10 three times.
|
||||
|
||||
**Expected result:**
|
||||
|
||||
Devices start beacon transmissions and send data on channel.
|
||||
|
||||
DUT2 scans channels for activity, and verifies that ED value for test channel is above 100.
|
||||
|
||||
The test exits with status `PASS` without timeouts.
|
||||
|
||||
### `send_data`
|
||||
|
||||
**Description:**
|
||||
|
||||
Send data between two devices over single RF hop.
|
||||
|
||||
Requires 2 devices.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
1. All devices have 802.15.4 tranceivers and are in sufficient proximity.
|
||||
2. RF channel is sufficiently clear.
|
||||
|
||||
**Test steps:**
|
||||
|
||||
1. DUT1: Set 64-bit MAC address to 01:02:03:00:00:00:00:01:
|
||||
`addr --64-bit 01:02:03:00:00:00:00:01`
|
||||
2. DUT2: Set 64-bit MAC address to 01:02:03:00:00:00:00:02:
|
||||
`addr --64-bit 01:02:03:00:00:00:00:02`
|
||||
3. DUT1: Start PAN coordinator:
|
||||
`start --pan_coordinator true --logical_channel <channel>`
|
||||
4. DUT2: Start PAN beacon:
|
||||
`start --pan_coordinator false --logical_channel <channel>`
|
||||
5. DUT1: Send data to DUT2:
|
||||
`data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde`
|
||||
6. DUT1: Send data to DUT2:
|
||||
`data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length 5 --msdu 12345`
|
||||
|
||||
**Expected result:**
|
||||
|
||||
Data send commands succeeds.
|
||||
|
||||
The test exits with status `PASS` without timeouts.
|
||||
|
||||
### `send_data_indirect`
|
||||
|
||||
**Description:**
|
||||
|
||||
Send data between two devices over two RF hops with one relay.
|
||||
|
||||
Requires 3 devices.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
1. All devices have 802.15.4 tranceivers and are in sufficient proximity.
|
||||
2. RF channel is sufficiently clear.
|
||||
|
||||
**Test steps:**
|
||||
|
||||
1. DUT1: Set 64-bit MAC address to 01:02:03:00:00:00:00:01:
|
||||
`addr --64-bit 01:02:03:00:00:00:00:01`
|
||||
2. DUT2: Set 64-bit MAC address to 01:02:03:00:00:00:00:02:
|
||||
`addr --64-bit 01:02:03:00:00:00:00:02`
|
||||
3. DUT3: Set 64-bit MAC address to 01:02:03:00:00:00:00:03:
|
||||
`addr --64-bit 01:02:03:00:00:00:00:03`
|
||||
4. DUT1: Start PAN coordinator:
|
||||
`start --pan_coordinator true --logical_channel <channel>`
|
||||
5. DUT2: Start PAN beacon:
|
||||
`start --pan_coordinator false --logical_channel <channel>`
|
||||
6. DUT3: Start PAN beacon:
|
||||
`start --pan_coordinator false --logical_channel <channel>`
|
||||
7. DUT2,DUT3: Set MAC RX on-while-idle off:
|
||||
`mlme-set --attr 0x52 --value_uint8 0 --value_size 1`
|
||||
8. DUT1: Add DUT2 and DUT3 as neighbours:
|
||||
`add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:02 --pan_id 0x1234 --index 0`
|
||||
`add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:03 --pan_id 0x1234 --index 1`
|
||||
9. DUT2,DUT3: Add DUT1 as neighbour:
|
||||
`add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0`
|
||||
10. DUT2: Configure indirect data:
|
||||
`config-status --data_ind abcde`
|
||||
10. DUT3: Configure indirect data:
|
||||
`config-status --data_ind 12345`
|
||||
11. DUT1,DUT2,DUT3: Mute traces (can cause timing issues):
|
||||
`silent-mode on`
|
||||
12. DUT1: Send data indirectly to DUT2:
|
||||
`data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde --indirect_tx true --wait_for_confirm false`
|
||||
13. DUT2: Poll DUT1 (coordinator) for data:
|
||||
`poll --coord_address 01:02:03:00:00:00:00:01`
|
||||
14. DUT1: Resend data twice:
|
||||
`data`
|
||||
`data`
|
||||
15. DUT2: Poll data twice:
|
||||
`poll`
|
||||
`poll`
|
||||
16. DUT2: Set expected poll return status to 0xEB (No data after poll):
|
||||
`config-status --poll 235")`
|
||||
17. DUT2: Expected fail for poll command:
|
||||
`poll`
|
||||
16. DUT2: Set expected poll return status to 0xEB (No data after poll):
|
||||
`config-status --poll 235")`
|
||||
17. DUT1: Send data indirectly to DUT3 (other values set before are preserved):
|
||||
`data --dst_addr 01:02:03:00:00:00:00:03 --msdu 12345`
|
||||
19. DUT2: Expected fail for poll command:
|
||||
`poll`
|
||||
20. DUT3: Poll DUT1 (coordinator) for data:
|
||||
`poll --coord_address 01:02:03:00:00:00:00:01`
|
||||
|
||||
**Expected result:**
|
||||
|
||||
DUT2 receives data from DUT1 when polling after send.
|
||||
|
||||
DUT2 does not receive data from DUT1 when no data available.
|
||||
|
||||
DUT2 does not receive data from DUT1 when data available for DUT3.
|
||||
|
||||
DUT3 receives data from DUT1 when polling after send.
|
||||
|
||||
The test exits with status `PASS` without timeouts.
|
||||
|
||||
### `send_large_payloads`
|
||||
|
||||
**Description:**
|
||||
|
||||
Send large payloads between two devices.
|
||||
|
||||
Requires 2 devices.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
1. All devices have 802.15.4 tranceivers and are in sufficient proximity.
|
||||
2. RF channel is sufficiently clear.
|
||||
|
||||
**Test steps:**
|
||||
|
||||
1. DUT1: Set 64-bit MAC address to 01:02:03:00:00:00:00:01:
|
||||
`addr --64-bit 01:02:03:00:00:00:00:01`
|
||||
2. DUT2: Set 64-bit MAC address to 01:02:03:00:00:00:00:02:
|
||||
`addr --64-bit 01:02:03:00:00:00:00:02`
|
||||
3. DUT1: Start PAN coordinator:
|
||||
`start --pan_coordinator true --logical_channel <channel>`
|
||||
4. DUT2: Start PAN beacon:
|
||||
`start --pan_coordinator false --logical_channel <channel>`
|
||||
5. Create large payload:
|
||||
104 characters, headers are 2+1+2+8+8+2=23 bytes, resulting in a packet size of 127 (max):
|
||||
`large_payload = "0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZZZZZZZZZ0123456789012345678901234567891234"`
|
||||
6. DUT1,DUT2: Set indirect data:
|
||||
`config-status --data_ind <large_payload>`
|
||||
7. DUT1: Send payload:
|
||||
`data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length <large_payload length> --msdu <large_payload>`
|
||||
8. DUT2: wait for transmission to finish:
|
||||
`wait --timeout 500`
|
||||
9. DUT2: send data :
|
||||
`data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length <large_payload length> --msdu <large_payload>`
|
||||
10. DUT1: wait for transmission to finish:
|
||||
`wait --timeout 500`
|
||||
11. DUT1,DUT2: Take turns waiting and transmitting:
|
||||
`data`
|
||||
`wait`
|
||||
|
||||
Test case repeats step 11 25 times for both devices.
|
||||
|
||||
**Expected result:**
|
||||
|
||||
Data send commands succeed.
|
||||
|
||||
The test exits with status `PASS` without timeouts.
|
||||
17
TEST_APPS/testcases/nanostack_mac_tester/__init__.py
Normal file
17
TEST_APPS/testcases/nanostack_mac_tester/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Copyright (c) 2020 Arm Limited
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
@@ -0,0 +1,82 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import os,sys
|
||||
from icetea_lib.bench import Bench
|
||||
from icetea_lib.TestStepError import TestStepFail
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self, name = "address_read_and_write",
|
||||
title = "MAC address and PAN id read/write test",
|
||||
status = "released",
|
||||
type = "smoke",
|
||||
subtype = "",
|
||||
execution = {
|
||||
"skip": {
|
||||
"value": False,
|
||||
"reason": ""
|
||||
}
|
||||
},
|
||||
author = "Valtteri Erkkila",
|
||||
purpose = "Tests reading a MAC address from the driver, and writing to the modifiable MAC address",
|
||||
feature = ["MLME-SET"],
|
||||
component = ["MAC"],
|
||||
requirements = {
|
||||
"duts": {
|
||||
'*': {
|
||||
"count":1,
|
||||
"type": "hardware",
|
||||
"allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW41Z"],
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-nanostack_mac_tester"
|
||||
}
|
||||
},
|
||||
"1":{"nick": "First"}
|
||||
}}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def do_test_iteration(self):
|
||||
self.command("First", "mlme-reset")
|
||||
self.command("First", "addr")
|
||||
self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01")
|
||||
self.command("First", "addr --16-bit 0xABCD")
|
||||
# Set MAC PAN ID
|
||||
self.command("First", "mlme-set --attr 0x50 --value_bytes CD:CD --value_size 2")
|
||||
self.command("First", "addr")
|
||||
self.verify_trace(1, "MAC64: 01:02:03:00:00:00:00:01")
|
||||
|
||||
def case(self):
|
||||
# Try tests few times because of potential RF failures
|
||||
loop = 0
|
||||
while loop < 5:
|
||||
try:
|
||||
self.do_test_iteration()
|
||||
break
|
||||
except TestStepFail:
|
||||
self.logger.info("Warning, iteration failed #" + str(loop+1))
|
||||
loop = loop + 1
|
||||
self.delay(5)
|
||||
else:
|
||||
raise TestStepFail("Too many failed iterations!")
|
||||
|
||||
def tearDown(self):
|
||||
self.reset_dut()
|
||||
|
||||
107
TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py
Normal file
107
TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py
Normal file
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import os,sys
|
||||
from icetea_lib.bench import Bench
|
||||
from icetea_lib.TestStepError import TestStepFail
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self, name = "create_and_join_PAN",
|
||||
title = "Create a PAN and have a device join it",
|
||||
status = "released",
|
||||
type = "smoke",
|
||||
subtype = "",
|
||||
execution = {
|
||||
"skip": {
|
||||
"value": False,
|
||||
"reason": ""
|
||||
}
|
||||
},
|
||||
author = "Valtteri Erkkila",
|
||||
purpose = "",
|
||||
feature = ["MLME-START", "MLME-SCAN (active)"],
|
||||
component = ["MAC"],
|
||||
requirements = {
|
||||
"duts": {
|
||||
'*': {
|
||||
"count":3,
|
||||
"type": "hardware",
|
||||
"allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW41Z"],
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-nanostack_mac_tester"
|
||||
}
|
||||
},
|
||||
"1":{"nick": "First"},
|
||||
"2":{"nick": "Second"},
|
||||
"3":{"nick": "Third"}
|
||||
}}
|
||||
)
|
||||
|
||||
def mask_from_channel_list(self, channels):
|
||||
res = 0
|
||||
for ch in channels:
|
||||
res = res | ( 1 << ch)
|
||||
return hex(res)
|
||||
|
||||
def setUp(self):
|
||||
self.channel = 11
|
||||
|
||||
def do_test_iteration(self):
|
||||
self.command("First", "mlme-reset")
|
||||
self.command("Second", "mlme-reset")
|
||||
self.command("Third", "mlme-reset")
|
||||
|
||||
# Beacon payload
|
||||
self.command("First", "mlme-set --attr 0x45 --value_ascii mac-tester --value_size 10")
|
||||
# Beacon payload length
|
||||
self.command("First", "mlme-set --attr 0x46 --value_uint8 10 --value_size 1")
|
||||
|
||||
self.command("Second", "mlme-set --attr 0x45 --value_ascii second-mac-tester --value_size 17")
|
||||
self.command("Second", "mlme-set --attr 0x46 --value_uint8 17 --value_size 1")
|
||||
|
||||
# Start PAN coordinator
|
||||
self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel))
|
||||
self.command("Second", "start --pan_coordinator true --logical_channel {}".format(int(self.channel)+1))
|
||||
self.delay(3)
|
||||
if self.channel == 11:
|
||||
channels = [11,12]
|
||||
elif self.channel == 26:
|
||||
channels = [25,26]
|
||||
else:
|
||||
channels = [self.channel, self.channel+1]
|
||||
self.command("Third", "scan --channel_mask {}".format(self.mask_from_channel_list(channels)))
|
||||
self.delay(0.2)
|
||||
self.command("Third", "find-beacon --data mac-tester")
|
||||
self.command("Third", "find-beacon --data second-mac-tester")
|
||||
|
||||
def case(self):
|
||||
# Try tests few times because of potential RF failures
|
||||
loop = 0
|
||||
while loop < 5:
|
||||
try:
|
||||
self.do_test_iteration()
|
||||
break
|
||||
except TestStepFail:
|
||||
self.logger.info("Warning, iteration failed #" + str(loop+1))
|
||||
loop = loop + 1
|
||||
self.delay(5)
|
||||
else:
|
||||
raise TestStepFail("Too many failed iterations!")
|
||||
|
||||
def tearDown(self):
|
||||
self.reset_dut()
|
||||
89
TEST_APPS/testcases/nanostack_mac_tester/send_data.py
Normal file
89
TEST_APPS/testcases/nanostack_mac_tester/send_data.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import os,sys
|
||||
from icetea_lib.bench import Bench
|
||||
from icetea_lib.TestStepError import TestStepFail
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self, name = "send_data",
|
||||
title = "Simple data transmission test",
|
||||
status = "released",
|
||||
type = "smoke",
|
||||
subtype = "",
|
||||
execution = {
|
||||
"skip": {
|
||||
"value": False,
|
||||
"reason": ""
|
||||
}
|
||||
},
|
||||
author = "Valtteri Erkkila",
|
||||
purpose = "Tests that sending data works",
|
||||
feature = ["MCPS-DATA"],
|
||||
component = ["MAC"],
|
||||
requirements = {
|
||||
"duts": {
|
||||
'*': {
|
||||
"count":2,
|
||||
"type": "hardware",
|
||||
"allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW41Z"],
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-nanostack_mac_tester"
|
||||
}
|
||||
},
|
||||
"1":{"nick": "First"},
|
||||
"2":{"nick": "Second"}
|
||||
}}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.channel = 11
|
||||
|
||||
def do_test_iteration(self):
|
||||
self.command("First", "mlme-reset")
|
||||
self.command("Second", "mlme-reset")
|
||||
|
||||
self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01")
|
||||
self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02")
|
||||
|
||||
# Start PAN coordinator
|
||||
self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel))
|
||||
# Start PAN beacon
|
||||
self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel))
|
||||
|
||||
# Send data
|
||||
self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde")
|
||||
self.command("Second", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length 5 --msdu 12345")
|
||||
|
||||
def case(self):
|
||||
# Try tests few times because of potential RF failures
|
||||
loop = 0
|
||||
while loop < 5:
|
||||
try:
|
||||
self.do_test_iteration()
|
||||
break
|
||||
except TestStepFail:
|
||||
self.logger.info("Warning, iteration failed #" + str(loop+1))
|
||||
loop = loop + 1
|
||||
self.delay(5)
|
||||
else:
|
||||
raise TestStepFail("Too many failed iterations!")
|
||||
|
||||
def tearDown(self):
|
||||
self.reset_dut()
|
||||
|
||||
134
TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py
Normal file
134
TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py
Normal file
@@ -0,0 +1,134 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import os,sys
|
||||
from icetea_lib.bench import Bench
|
||||
from icetea_lib.TestStepError import TestStepFail
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self, name = "send_data_indirect",
|
||||
title = "Indirect data transmission test",
|
||||
status = "released",
|
||||
type = "smoke",
|
||||
subtype = "",
|
||||
execution = {
|
||||
"skip": {
|
||||
"value": False,
|
||||
"reason": ""
|
||||
}
|
||||
},
|
||||
author = "Valtteri Erkkila",
|
||||
purpose = "Tests sending data indirectly, i.e polling the coordinator for data",
|
||||
feature = ["MCPS-DATA", "MLME-POLL"],
|
||||
component = ["MAC"],
|
||||
requirements = {
|
||||
"duts": {
|
||||
'*': {
|
||||
"count":3,
|
||||
"type": "hardware",
|
||||
"allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW41Z"],
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-nanostack_mac_tester"
|
||||
}
|
||||
},
|
||||
"1":{"nick": "First"},
|
||||
"2":{"nick": "Second"},
|
||||
"3":{"nick": "Third"}
|
||||
}}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.channel = 11
|
||||
|
||||
def do_test_iteration(self):
|
||||
self.channel = 11
|
||||
self.command("First", "mlme-reset")
|
||||
self.command("Second", "mlme-reset")
|
||||
self.command("Third", "mlme-reset")
|
||||
|
||||
self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01")
|
||||
self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02")
|
||||
self.command("Third", "addr --64-bit 01:02:03:00:00:00:00:03")
|
||||
|
||||
# Start PAN coordinator
|
||||
self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel))
|
||||
# Start PAN beacon
|
||||
self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel))
|
||||
self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel))
|
||||
|
||||
# Set MAC RX on-while-idle off
|
||||
self.command("Second", "mlme-set --attr 0x52 --value_uint8 0 --value_size 1")
|
||||
self.command("Third", "mlme-set --attr 0x52 --value_uint8 0 --value_size 1")
|
||||
|
||||
# Add neighbours
|
||||
self.command("First", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:02 --pan_id 0x1234 --index 0")
|
||||
self.command("First", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:03 --pan_id 0x1234 --index 1")
|
||||
self.command("Second", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0")
|
||||
self.command("Third", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0")
|
||||
|
||||
# Configure indirect data
|
||||
self.command("Second", "config-status --data_ind abcde")
|
||||
self.command("Third", "config-status --data_ind 12345")
|
||||
|
||||
# Runs into timing issues if extensive printing is enabled
|
||||
self.command("*", "silent-mode on")
|
||||
|
||||
# Send data indirectly to DUT2
|
||||
self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde --indirect_tx true --wait_for_confirm false")
|
||||
# Poll DUT1 for data
|
||||
self.command("Second", "poll --coord_address 01:02:03:00:00:00:00:01")
|
||||
|
||||
# Send more data
|
||||
self.command("First", "data")
|
||||
self.command("First", "data")
|
||||
# Poll more data
|
||||
self.command("Second", "poll")
|
||||
self.command("Second", "poll")
|
||||
# Set expected poll return status to 0xEB(no data after poll)
|
||||
self.command("Second", "config-status --poll 235")
|
||||
# No data should remain to be polled for
|
||||
self.command("Second", "poll")
|
||||
|
||||
# Set expected poll return status to 0xEB(no data after poll)
|
||||
self.command("Second", "config-status --poll 235")
|
||||
# Send data to DUT3
|
||||
self.command("First", "data --dst_addr 01:02:03:00:00:00:00:03 --msdu 12345")
|
||||
# Poll(expected failure)
|
||||
self.command("Second", "poll")
|
||||
# Poll DUT1 for data(expected success)
|
||||
self.command("Third", "poll --coord_address 01:02:03:00:00:00:00:01")
|
||||
self.command("*", "silent-mode off")
|
||||
|
||||
def case(self):
|
||||
# Try tests few times because of potential RF failures
|
||||
loop = 0
|
||||
while loop < 5:
|
||||
try:
|
||||
self.do_test_iteration()
|
||||
break
|
||||
except TestStepFail:
|
||||
self.logger.info("Warning, iteration failed #" + str(loop+1))
|
||||
loop = loop + 1
|
||||
self.delay(5)
|
||||
else:
|
||||
raise TestStepFail("Too many failed iterations!")
|
||||
|
||||
def tearDown(self):
|
||||
self.command("*", "silent-mode off")
|
||||
self.reset_dut()
|
||||
|
||||
107
TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py
Normal file
107
TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py
Normal file
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import os,sys
|
||||
from icetea_lib.bench import Bench
|
||||
from icetea_lib.TestStepError import TestStepFail
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self, name = "send_large_payloads",
|
||||
title = "Data transmission test with large packets",
|
||||
status = "released",
|
||||
type = "reliability",
|
||||
subtype = "",
|
||||
execution = {
|
||||
"skip": {
|
||||
"value": False,
|
||||
"reason": ""
|
||||
}
|
||||
},
|
||||
author = "Valtteri Erkkila",
|
||||
purpose = "Repeatedly sends long packets, checking that the payload is correct in each one",
|
||||
feature = ["MCPS-DATA"],
|
||||
component = ["MAC"],
|
||||
requirements = {
|
||||
"duts": {
|
||||
'*': {
|
||||
"count":2,
|
||||
"type": "hardware",
|
||||
"allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW41Z"],
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-nanostack_mac_tester"
|
||||
}
|
||||
},
|
||||
"1":{"nick": "First"},
|
||||
"2":{"nick": "Second"}
|
||||
}}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.channel = 11
|
||||
|
||||
def do_test_iteration(self):
|
||||
self.command("First", "mlme-reset")
|
||||
self.command("Second", "mlme-reset")
|
||||
|
||||
self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01")
|
||||
self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02")
|
||||
|
||||
#104 characters, headers are 2+1+2+8+8+2=23 bytes, resulting in a packet size of 127 (max)
|
||||
large_payload = "0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZZZZZZZZZ0123456789012345678901234567891234"
|
||||
# Start PAN coordinator
|
||||
self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel))
|
||||
# Start PAN beacon
|
||||
self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel))
|
||||
|
||||
# Set indirect data
|
||||
self.command("First", "config-status --data_ind {}".format(large_payload))
|
||||
self.command("Second", "config-status --data_ind {}".format(large_payload))
|
||||
|
||||
# Send data to DUT2
|
||||
self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length {} --msdu {}".format(len(large_payload), large_payload))
|
||||
# Wait for transmission to finish
|
||||
self.command("Second", "wait --timeout 500")
|
||||
|
||||
# Send data to DUT1
|
||||
self.command("Second", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length {} --msdu {}".format(len(large_payload), large_payload))
|
||||
# Wait for transmission to finish
|
||||
self.command("First", "wait --timeout 500")
|
||||
# Loop with previous settings
|
||||
for i in range(0, 25):
|
||||
self.command("First", "data")
|
||||
self.command("Second", "wait")
|
||||
self.command("Second", "data")
|
||||
self.command("First", "wait")
|
||||
|
||||
def case(self):
|
||||
# Try tests few times because of potential RF failures
|
||||
loop = 0
|
||||
while loop < 5:
|
||||
try:
|
||||
self.do_test_iteration()
|
||||
break
|
||||
except TestStepFail:
|
||||
self.logger.info("Warning, iteration failed #" + str(loop+1))
|
||||
loop = loop + 1
|
||||
self.delay(5)
|
||||
else:
|
||||
raise TestStepFail("Too many failed iterations!")
|
||||
|
||||
def tearDown(self):
|
||||
self.reset_dut()
|
||||
|
||||
65
TEST_APPS/testcases/nanostack_mac_tester/template
Normal file
65
TEST_APPS/testcases/nanostack_mac_tester/template
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
import os,sys
|
||||
# ensure that test/ directory is first choice for imports
|
||||
test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
if not test_dir in sys.path:
|
||||
sys.path.insert(0, test_dir)
|
||||
from GenericTestcase import Bench
|
||||
from Error import TestStepFail
|
||||
|
||||
class Testcase(Bench):
|
||||
def __init__(self):
|
||||
Bench.__init__(self, name = "template", # Name should be the same as the filename
|
||||
title = "TITLE",
|
||||
status = "development",
|
||||
type = "TYPE",
|
||||
subtype = "",
|
||||
execution = {
|
||||
"skip": {
|
||||
"value": False,
|
||||
"reason": ""
|
||||
}
|
||||
},
|
||||
author = "Valtteri Erkkila",
|
||||
purpose = "",
|
||||
feature = [""],
|
||||
component = ["MAC"],
|
||||
requirements = {
|
||||
"duts": {
|
||||
'*': {
|
||||
"count":2, # Count must reflect the amount of specified devices below
|
||||
"type": "hardware",
|
||||
"application":{ "name":"generalTestApplication", "version": "1.0"},
|
||||
"rf_channel": 11
|
||||
},
|
||||
"1":{"nick": "First"},
|
||||
"2":{"nick": "Second"}
|
||||
}}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def case(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
if __name__=='__main__':
|
||||
sys.exit( Testcase().run() )
|
||||
336
TEST_APPS/testcases/nfc/README.md
Normal file
336
TEST_APPS/testcases/nfc/README.md
Normal file
@@ -0,0 +1,336 @@
|
||||
# NFC tests.
|
||||
|
||||
A CI test suite for NFC component. These tests validate card mbed emulation cases. The key use case is an NFC smart poster supporting comissioning workflow.
|
||||
The SUT (system under test) is the NFC target. Tests exercise the framework and NDEF transactions when a NFC controller driver is used, or when the stack is configured for an NFC EEPROM chip in the system integration.
|
||||
|
||||
This project is called CreamScone, which is an ice tea framework based cli-driven python test.
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [NFC tests.](#nfc-tests)
|
||||
- [Overview](#overview)
|
||||
- [System Test high level requirement](#system-test-high-level-requirement)
|
||||
- [Low level design](#low-level-design)
|
||||
- [User Guide](#user-guide)
|
||||
- [Test cases](#test-cases)
|
||||
- [cli commands](#cli-commands)
|
||||
- [How to](#how-to)
|
||||
- [Running the tests](#running-the-tests)
|
||||
- [Alternate NFC drivers note:](#alternate-nfc-drivers-note)
|
||||
- [Known issues](#known-issues)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
# Overview
|
||||
A set of tests run in CI, which can provide:
|
||||
- Internal confidence
|
||||
- Faster iterations
|
||||
- More efficient work
|
||||
- Clear escalation path
|
||||
|
||||
A [Ice-tea](https://github.com/ARMmbed/mbed-os-5-docs/blob/development/docs/tools/testing/testing_icetea.md) based test suite. In it's simplest form, the suite merely drives API's for the NFC tag reader/writer, and validates a tag simulation running on an idle target, allows test cases in discovery, connection and read/write NDEF records.
|
||||
|
||||
In order to mitigate the costs associated with system testing, use existing frameworks or parts and make it easy to test each individually. The [nfcpy](https://nfcpy.readthedocs.io/) Python library is used as the core of the *CreamScone* component which uses a PN53* device [SCL3711 usb reader](https://www.identiv.com/products/smart-card-readers/rfid-nfc-contactless/scl3711/) over USB to read the mbed simulated tag. This library is used to drive host interactions because it is portable (windows/GNULinux.) Remote NFC interactions will raise events in the mbed application. Connection and read/write events which get handled in user application on the target get wired up to asynchronously return responses and the data values (NDEF messages) to the ice tea framework. These events and data are thus tested/checked in the code (python) running on host. The target test app wraps the API, allowing many new test-scenarios to be written purely in Python.
|
||||
|
||||
**NFC compliance**
|
||||
|
||||
This suite only assists in NFC forum compliance. Developers must self certify using test tools from a provider to uncover early issues and get an external test vendor to achieve certification.
|
||||
|
||||
**Mobiles and inter-op**
|
||||
|
||||
Not in scope. Test procedures using a mobile phone app for Android and for IOS were not in scope for this test suite.
|
||||
|
||||

|
||||
|
||||
Because the comissioning workflow application quality is the end goal, the NFC suite includes learnings to design the CI setup needed for future system testing that bring a mobile phone into the test-rig. The use of a mobile and bluetooth pairing as well as the continous integration system is not included.
|
||||
|
||||
|
||||
|
||||
# System Test high level requirement
|
||||
Mitigate risks identified, to the product from an internal view to supporting releases. Help customers develop a driver or a design, and reduce their production risks. In summary:
|
||||
- Architecture risks and Api breaks
|
||||
- Partner cannot NFC forum Certify
|
||||
- Partner driver has bugs
|
||||
- Code regressions in O/S
|
||||
- Arm mbed provided driver or HAL has bugs
|
||||
- Security vulnerabilities
|
||||
|
||||
In short, “Empower engineers to efficiently ship quality code with confidence.”
|
||||
|
||||
**Design requirements: **
|
||||
- Identify and use tools to allow running in CI system, on many targets/configurations
|
||||
- Be portable (can run in the CI system) using NFC explorer boards in lab for correctly co-located targets.
|
||||
- Be able to set up and run locally in development
|
||||
|
||||
# Low level design
|
||||
**Components**
|
||||
|
||||
API standalone Self tests [test_self.py](TEST_APPS\testcases\nfc\test_self.py)
|
||||
|
||||
API E2E (wireless) tests [test_nfc.py](TEST_APPS\testcases\nfc\test_nfc.py)
|
||||
|
||||
An [icetea](https://github.com/ARMmbed/icetea/blob/master/README.md) framework test program. Commandline (serial port) driven [target app](TEST_APPS\devices\nfcapp\main.cpp) aka _'CreamScone'_ which allows manual interactions with the driver. The app will send all API return data over serial link.
|
||||
|
||||
An icetea plugin [nfc_test_parsers.py](TEST_APPS\icetea_plugins\nfc_test_parsers.py) which parses API responses over the serial port into python variables.
|
||||
|
||||
MbedOS cli test app [main.cpp](TEST_APPS\device\nfcapp\main.cpp). The CLI commands return results asynchronously for most commands which get passed to and handled on a driver thread.
|
||||
|
||||
**Future: ** A complete inter-op ready design intended to include a switch-box to allow the reader to connect to NFC enabled targets nearby using flying cables and a sticky-back antenna. The switch should allow selecting either alternative tags, or NFC peers, and provide inter-operability coverage. The switch-box may be controlled using GPIO either driven from spare IO pins on the target DUT itself (preferred option), or perhaps from a Raspberry pi.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
** Reference: **
|
||||
|
||||
[ARMmbed NFC design](https://github.com/ARMmbed/mbed-os/blob/master/docs/design-documents/nfc/nfc_design.md)
|
||||
|
||||
[ARMmbed NFC code](https://github.com/ARMmbed/mbed-os/tree/master/features/nfc/nfc)
|
||||
|
||||
[ARMmbed NFC example application](https://github.com/ARMmbed/mbed-os-example-nfc/)
|
||||
|
||||
[Python NFC library](https://nfcpy.readthedocs.io/en/latest/topics/get-started.html)
|
||||
|
||||
[NFC forum](https://nfc-forum.org/)
|
||||
|
||||
# User Guide
|
||||
This section covers the test case specification and how to run the test suite.
|
||||
|
||||
The SUT target is rebooted between tests, since tests modify the target hardware state.
|
||||
|
||||
## Test cases
|
||||
CLI commands used by each test case describe the steps in a test.
|
||||
** Basic local only cases **
|
||||
- test_nfc_error_codes : inintnfc , setlastnfcerror \<n> , getlastnfcerror
|
||||
- Verify that the test CLI engine can initialize the stack, and can return NFC codes
|
||||
- test_nfc_eeprom : iseeprom
|
||||
- prints "true" if the target has an EEPROM configured stack, else prints "false" diagnostic only
|
||||
- test_nfc_get_controller_protocols
|
||||
- set nfc protocols supported
|
||||
- test_nfc_set_controller_protocols
|
||||
- get nfc protocols supported
|
||||
- test_nfc_setsmartposter : setsmartposter \<-u> \<url>
|
||||
- Sets a smartposter message, does not verify over wireless! (Only https protocol tested.)
|
||||
- test_nfc_erase : initnfc, erase, readmessage
|
||||
- Erase entire EEPROM, (behaviour for controller stack is null)
|
||||
- test_nfc_write_long : initnfc, erase, writelong \<n>, readmessage
|
||||
- Write a very large text T record, and verify expected length written
|
||||
- test_nfc_reprogrammed : iseeprom, initnfc, erase, wirelessly reprogram, wirelessly verify
|
||||
- Use a reader/writer to program the tag using the default M24SR chip password
|
||||
** End-to-End cases **
|
||||
- test_nfce2e_target_found
|
||||
- tag can actually be detected wireless
|
||||
test_nfce2e_type4_found
|
||||
- correct tag detected
|
||||
- test_nfce2e_discovery_loop
|
||||
- Start or Stop discovery loop or disable depending on stack
|
||||
test_nfce2e_read_stress
|
||||
- read large message from device
|
||||
test_nfce2e_reprogrammed
|
||||
- modify large message from device
|
||||
test_nfce2e_reprogrammed_stress
|
||||
- write moderate message wirelessly
|
||||
test_nfce2e_smartposter
|
||||
- as with the basic test, but wirelessly
|
||||
|
||||
** unimplemented due to limited support **
|
||||
- test_nfc_iso7816_supported
|
||||
- test_nfc_add_iso7816_application
|
||||
- test_nfc_set_tagtype
|
||||
- test_nfc_get_tagtype
|
||||
|
||||
|
||||
## cli commands
|
||||
cli commands take parameters, its possible to type help at the cli for a list of commands.
|
||||
```
|
||||
mbed sterm --baudrate 115200
|
||||
help
|
||||
...
|
||||
getlastnfcerror last NFC error code
|
||||
setlastnfcerror self-test
|
||||
initnfc init NFC driver
|
||||
setsmartposter send smartposter NDEF
|
||||
iseeprom NFC configEEPROM present
|
||||
readmessage read EEPROM else return last message
|
||||
erase erase EEPROM or clear last message
|
||||
writelong fill entire FILE with pattern
|
||||
...
|
||||
```
|
||||
Note: Most commands also return a NFC status value (type "getlastnfcerror help" in console) which allow us to build negative test cases.
|
||||
Note: Some commands only apply to NFC controllers, these commands fail with the appropriate not-supported code NFC_ERR_UNSUPPORTED and additionally return -2 error code to ice-tea. Commands like the erase command is a no-op on a NFC Controller target in the test app, for test-writting convenience.
|
||||
|
||||
|
||||
**unimplemented CLI commands**
|
||||
commands that were not implemented in the test app
|
||||
- set/get tag type
|
||||
- get/set iso7816 app
|
||||
|
||||
**Excluded**
|
||||
- power consumption
|
||||
- throughput
|
||||
- memory consumption
|
||||
|
||||
## How to
|
||||
**Wirring diagram for NFC Explorer with PN512**
|
||||
|
||||
If using the Raspbery Pi explorer (PN512) board, use this pinout mapping diagram to connect the shield to the reference target. In this case a ST NucleoF401RE pinout is shown.
|
||||
```
|
||||
Nucleo F401RE Explore NFC
|
||||
(Arduino header) (pin1 on shield shown with a <|)
|
||||
+-------+ +-------+ +--------+
|
||||
| [NC] | | [B8] | |[ 2][ 1]|
|
||||
| [IOREF| | [B9] | |[ 4][ 3]|
|
||||
| [RST] | | [AVDD]| |[ 6][ 5]|
|
||||
1<---+ [3V3] | | [GND] | |[ 8][ 7]|
|
||||
| [5V] | | [A5] +--->23 |[10][ 9]|
|
||||
| [GND] | | [A6] +--->21 |[12][11]|
|
||||
25<--+ [GND] | | [A7] +--->19 |[14][13]|
|
||||
| [VIN] | | [B6] +--->3 |[16][15]|
|
||||
| | | [C7] | |[18][17]|
|
||||
26<--+ [A0] | | [A9] | |[20][19]|
|
||||
16<--+ [A1] | | [A9] | |[22][21]|
|
||||
| ... | | | |[24][23]|
|
||||
| | | [A8] | |[26][25]|
|
||||
+-------+ | ... | +--------+
|
||||
| |
|
||||
| |
|
||||
+-------+
|
||||
|
||||
Patch using jumper wires to the
|
||||
indicated pins on the Shield.
|
||||
|
||||
```
|
||||
Schematic (https://www.element14.com/community/docs/DOC-76384/l/explore-nfc-board-schematic)
|
||||
To change pinouts, if your reference design or shield pins differ for the PN512 controller driver, open nfcProcessCtrl.cpp and find the code
|
||||
```json
|
||||
NFCProcessController::NFCProcessController(events::EventQueue &queue) :
|
||||
// pins: mosi, miso, sclk, ssel, irq, rst
|
||||
_pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver(
|
||||
&_pn512_transport), _queue(queue), _nfc_controller(
|
||||
&_pn512_driver, &queue, _ndef_buffer) {
|
||||
}
|
||||
```
|
||||
modify pins as needed.
|
||||
|
||||
**Compilation target drivers**
|
||||
|
||||
If using the EEPROM driver, the mbed_app.json will contain
|
||||
```json
|
||||
"target_overrides": {
|
||||
"DISCO_L475VG_IOT01A": {
|
||||
"target.extra_labels_add": ["M24SR"]
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
If using the Explorer Shield or PN512 driver mbed_app.json will add
|
||||
```json
|
||||
"target_overrides": {
|
||||
"NUCLEO_F401RE": {
|
||||
"target.extra_labels_add": ["PN512"]
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
## Running the tests
|
||||
1. Wire an [explorer shield](https://cpc.farnell.com/nxp/explore-nfc/add-on-board-nfc-for-raspberry/dp/SC13404) up to and compile the target application.
|
||||
2. Flash the binary to target and verify that it responds with an _`action NDEF record http://www.mbed.com`_ by using a mobile phone to scan over the antenna.
|
||||
3. Install python (2.7) and install the nfcpy library, [see](https://nfcpy.readthedocs.io/en/latest/topics/get-started.html) . NFC reader can be connected to a serial port, or more commonly a USB dongle. Verify the dongle is functioning.
|
||||
4. Place the scanner near the explorer shield. Run various test program commands like so:
|
||||
- python ice_device -command describe
|
||||
|
||||
**run the suite**
|
||||
In a working folder, run
|
||||
|
||||
`git clone https://github.com/ARMmbed/mbed-os.git`
|
||||
|
||||
|
||||
If using the EEPROM driver, ( ST Discovery and the M24SR driver), you need the driver:
|
||||
`git clone https://github.com/ARMmbed/mbed-nfc-m24sr.git'
|
||||
|
||||
|
||||
And copy the files into your mbed root:
|
||||
`xcopy ..\mbed-nfc-m24sr\*.* .\eeprom_driver\'
|
||||
|
||||
To run the End2End tests, type:
|
||||
`mbed test --icetea --app-config .\TEST_APPS\device\nfcapp\mbed_app.json -n test_nfce2e`
|
||||
|
||||
To run only the standalone (readerless tests if you do not have a card reader), type:
|
||||
`mbed test --icetea --app-config .\TEST_APPS\device\nfcapp\mbed_app.json -n test_nfc_eeprom,test_nfc_error_codes,test_nfc_setsmartposter,test_nfc_erase,test_nfc_write_long`
|
||||
|
||||
# Alternate NFC drivers note:
|
||||
|
||||
Please see the example json file .\TEST_APPS\testcases\nfc\mbed_app.json . The test does not check that you have any needed shield installed, so if it "hangs" at the point the "initnfc" command is used, the driver or shield may be the fault. The test assumes that MBED_CONF_NFCEEPROM is set to 1, if not it assumes that a NFC Controller driver is in use. To test drivers other than PN512 and M24SR, it is required to make test code changes that reference the driver. The driver can be instantiated once only.
|
||||
|
||||
If the new driver you add is for Eeprom, open nfccommands.cpp and find the code and modify line as shown +++
|
||||
```C++
|
||||
NFCTestShim* new_testshim() {
|
||||
#if MBED_CONF_NFCEEPROM
|
||||
--- mbed::nfc::NFCEEPROMDriver& eeprom_driver = get_eeprom_driver(nfcQueue);
|
||||
|
||||
+++ mbed::nfc::NFCEEPROMDriver& eeprom_driver = get_myeeprom_driver(nfcQueue);
|
||||
|
||||
return ( (NFCTestShim *)(new NFCProcessEEPROM(nfcQueue, eeprom_driver)) );
|
||||
#else
|
||||
return ((NFCTestShim *) (new NFCProcessController(nfcQueue)));
|
||||
#endif // EEPROM
|
||||
```
|
||||
|
||||
If the driver you add is a Controller driver, open nfcProcessCtrl.cpp and find the code
|
||||
```C++
|
||||
NFCProcessController::NFCProcessController(events::EventQueue &queue) :
|
||||
// pins: mosi, miso, sclk, ssel, irq, rst
|
||||
_pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver(
|
||||
&_pn512_transport), _queue(queue), _nfc_controller(
|
||||
&_pn512_driver, &queue, _ndef_buffer) {
|
||||
}
|
||||
```
|
||||
1. You will want to replace this and reference the desired controller driver. Likewise, this code is where pinout changes have to be made if using the supplied Controller driver.
|
||||
2. Search for occurences of guard macros `#ifdef TARGET_PN512` , and `#endif`. Add a new guard macro and code for your specific controller driver at the same point.
|
||||
|
||||
|
||||
Note: If the target uses an EEPROM, it need not be powered/running, to be read, mbedOS is not running at that point.
|
||||
|
||||
**Device API error codes**
|
||||
|
||||
You can issue the command "getlastnfcerror help" to see a list of error codes that are returned by most commands.
|
||||
```C++
|
||||
#define NFC_OK 0 ///< No error
|
||||
#define NFC_ERR_UNKNOWN 1 ///< Unknown error
|
||||
#define NFC_ERR_LENGTH 2 ///< Length of parameter is wrong
|
||||
#define NFC_ERR_NOT_FOUND 3 ///< Could not find item
|
||||
#define NFC_ERR_UNSUPPORTED 4 ///< This action is not supported
|
||||
#define NFC_ERR_PARAMS 5 ///< These parameters are not correct
|
||||
#define NFC_ERR_BUFFER_TOO_SMALL 6 ///< The buffer is too small to store all data (buffer overflow)
|
||||
#define NFC_ERR_TIMEOUT 7 ///< Timeout
|
||||
#define NFC_ERR_CRC 8 ///< Checksum does not match
|
||||
#define NFC_ERR_NOPEER 9 ///< No target/initiator in vicinity
|
||||
#define NFC_ERR_PARITY 10 ///< Parity error
|
||||
#define NFC_ERR_FIELD 11 ///< No RF field detected (or RF field lost)
|
||||
#define NFC_ERR_COLLISION 12 ///< Collision detected
|
||||
#define NFC_ERR_WRONG_COMM 13 ///< Communication error
|
||||
#define NFC_ERR_PROTOCOL 14 ///< Protocol is not conformant
|
||||
#define NFC_ERR_BUSY 15 ///< Resource is busy
|
||||
#define NFC_ERR_CONTROLLER 16 ///< Controller failure
|
||||
#define NFC_ERR_HALTED 17 ///< Target has been halted
|
||||
#define NFC_ERR_MAC 18 ///< MAC does not match
|
||||
#define NFC_ERR_UNDERFLOW 19 ///< Could not send data in time
|
||||
#define NFC_ERR_DISCONNECTED 20 ///< Link has disconnected
|
||||
#define NFC_ERR_ABORTED 21 ///< Command was aborted
|
||||
```
|
||||
# Known issues
|
||||
|
||||
1. The test app defines large buffer to store the maximum realistic message of 8K by default. For targets with limited memory (< ~32K) will need to modify the app config. Open mbed_app.config and modify the setting
|
||||
` "TEST_NDEF_MSG_MAX" : 8192` to suit by overriding it on specific targets. The test cases (python code) which stress read/write will need updates if the buffer is reduced to 2K by editing test_nfc.py and modifying the line(s) to fall within the new macro value.
|
||||
```python
|
||||
# Values > 4 k incur large time costs
|
||||
STRESS_BUFFLEN = 2050
|
||||
```
|
||||
|
||||
2. The test app and the supplied drivers only support Type4 tags. The test app thus does not exercise the different protocols and always sets iso-dep level functionality (Type4) for NFC Controller initialization.
|
||||
|
||||
1. Test test_nfce2e_discovery_loop fails on NFC controller. The NFC controller driver discovery loop cannot be stopped manually. No major functionality is lost, it only prevents a complete disable of NFC at runtime. A bug ticket #IOTPAN-313 was logged to fix the stop function. The Controller still restarts discovery loop normally under app control after a peer disconnects.
|
||||
|
||||
1. The smartposter NDEF record wrapper class `smartposter.h` is also provided as part of the NFC examples. The examples are not needed to compile the test app, but this example class may be usefull to customers. This file may thus move into the NFC component in future.
|
||||
16
TEST_APPS/testcases/nfc/__init__.py
Normal file
16
TEST_APPS/testcases/nfc/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Copyright (c) 2020 Arm Limited
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
BIN
TEST_APPS/testcases/nfc/img/creamscone-mobile.png
Normal file
BIN
TEST_APPS/testcases/nfc/img/creamscone-mobile.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
TEST_APPS/testcases/nfc/img/inter-op-view.png
Normal file
BIN
TEST_APPS/testcases/nfc/img/inter-op-view.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
TEST_APPS/testcases/nfc/img/simple-overview.png
Normal file
BIN
TEST_APPS/testcases/nfc/img/simple-overview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
94
TEST_APPS/testcases/nfc/mobileapi.md
Normal file
94
TEST_APPS/testcases/nfc/mobileapi.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# NFC test SDK comparison
|
||||
|
||||
A comparison of the SDKs exposed to manage NFC tags on Android IOs and Python module PyNFC.
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [NFC test SDK comparison](#nfc-test-sdk-comparison)
|
||||
- [Overview](#overview)
|
||||
- [Comparison](#comparison)
|
||||
- [IOS (objective C)](#ios-objective-c)
|
||||
- [Android (Java)](#android-java)
|
||||
- [pynfc (python)](#pynfc-python)
|
||||
- [Observe](#observe)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
# Overview
|
||||
A comparison which analyses NFC use cases on mobile, as background to the test case design/implementation in the comissioning workflow :NFC-Bluetooth-pairing application.
|
||||
- Analyse the [Apple API](https://developer.apple.com/documentation/corenfc)
|
||||
- Analyse the [Android API](https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#java)
|
||||
- Python test [pynfc modules](https://nfcpy.readthedocs.io/en/latest/modules/index.html)
|
||||
|
||||
|
||||
# Comparison
|
||||
From the lowest level, each programmer interface has a definition for Errors, Link, NDEF, Tags and Sessions.
|
||||
Note: Comparisons are high level and use past experience and old docs.
|
||||
|
||||
|
||||
## IOS (objective C)
|
||||
1. Errors:
|
||||
are a struct with 3 members:<BR>
|
||||
int:value, <BR>struct:string:usererror<BR>struct:string:localized
|
||||
1. isodep:
|
||||
|
||||
not supported
|
||||
1. NDEF support parameters with
|
||||
|
||||
payload<BR>and Typename,
|
||||
4. Tags:
|
||||
|
||||
Are an Object representing tag
|
||||
5. Sessions are managed using
|
||||
|
||||
Delegate &callbacks
|
||||
|
||||
## Android (Java)
|
||||
1. Errors:
|
||||
|
||||
thrown as IOException with cause and message, there are no returned error datas
|
||||
2. isodep:
|
||||
|
||||
get/set communication parameters and RAW.
|
||||
3. NDEF:
|
||||
|
||||
Includes SNEP
|
||||
4. Tags :
|
||||
|
||||
(3)A,(3)B,(4)F,V and isoDep layer
|
||||
5. Sessions
|
||||
|
||||
Intents and Actions, runtime registration of PendingIntent() allows hooking using tag filters
|
||||
|
||||
## pynfc (python)
|
||||
1. Errors :
|
||||
|
||||
raises Exceptions nfc.clf.Error and others per class
|
||||
2. isodep:
|
||||
|
||||
get/set communication parameters and RAW.
|
||||
3. NDEF:
|
||||
|
||||
full implementation and types. Includes SNEP
|
||||
4. Tags :
|
||||
|
||||
1,2,(3)A,(3)B,(4)F isoDep layer
|
||||
5. Sessions :
|
||||
|
||||
using delegate class callbacks
|
||||
|
||||
# Observe
|
||||
Negative test cases would be better designed around the user cases, than around the implementations, base error conditions at the API layer look more like
|
||||
- UnsupportedTarget
|
||||
- Communication
|
||||
- Protocol
|
||||
- (w) Transmission
|
||||
- (w) Timeout
|
||||
- (w) BrokenLink
|
||||
- ValueError
|
||||
|
||||
Valuable test data cases shall be for valid and boundary cases for the smartposter NDEF record:
|
||||
- uri – URI string ASCII only
|
||||
- title – Smart poster title(s), (additional internationalizations with IANA codes not tested)
|
||||
- icons – omitted
|
||||
- action – The recommended action , a string
|
||||
98
TEST_APPS/testcases/nfc/nfc_clf_wrapper.py
Normal file
98
TEST_APPS/testcases/nfc/nfc_clf_wrapper.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import time
|
||||
import nfc
|
||||
from nfc.clf import RemoteTarget
|
||||
import logging
|
||||
|
||||
"""
|
||||
Wrap calls to nfcpi testing module, handle loading the driver
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def debug_nfc_data(key, value):
|
||||
"""
|
||||
print useful data values for the host/user {{in between}} easy to spot brackets.
|
||||
"""
|
||||
text = "{{%s=%s}}" % (key, value)
|
||||
logger.info(text)
|
||||
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class NfcWrapper:
|
||||
"""
|
||||
Finds the NFC reader USB front-end and prepares it for use.
|
||||
"""
|
||||
def __init__(self):
|
||||
# will need a parameter here to help with libusb detection?
|
||||
# clf.open("usb:04e6:5591") will open the SCL3711-NFC
|
||||
logger.info("Initializing the NFC tag reader...")
|
||||
self.clf = nfc.ContactlessFrontend()
|
||||
if (self.clf.open("usb") ): # the NFC reader was not detected on any USB port!
|
||||
logger.info("NFC Frontend found OK")
|
||||
else:
|
||||
logger.error("The NFC reader was not detected on any USB port!")
|
||||
self.clfResponse = None
|
||||
|
||||
def connect(self):
|
||||
# note: only supporting type4
|
||||
time.sleep(0.5)
|
||||
after5s = lambda: time.time() - started > 5
|
||||
started = time.time()
|
||||
|
||||
tag = self.clf.connect( rdwr={'on-connect': lambda tag: False},
|
||||
terminate = after5s)
|
||||
if tag: # None if timeout expires
|
||||
logging.info("NFCReader: connected " + str(tag))
|
||||
else:
|
||||
logging.info("NFCReader: warning, no tag detected ")
|
||||
return tag
|
||||
|
||||
def mute(self):
|
||||
"""turn off the reader radio"""
|
||||
if (self.clf.device is not None):
|
||||
logging.info("NFCReader: radio mute" + self.clf.device.product_name)
|
||||
self.clf.device.mute()
|
||||
else:
|
||||
logging.warning("NFCReader: reader not initialized!")
|
||||
|
||||
def disconnect(self):
|
||||
logging.info("NFCReader: close frontend.")
|
||||
self.clf.close()
|
||||
|
||||
"""
|
||||
Handle interactions with the NFC reader, and singleton
|
||||
"""
|
||||
class ContactlessCommandRunner():
|
||||
|
||||
"""
|
||||
Lazy initialization singleton to open the reader once only - else when the framework scans for
|
||||
tests, it causes us to open the reader. This breaks the Windows driver.
|
||||
"""
|
||||
def __getattr__(self, name):
|
||||
if name == 'nfc':
|
||||
|
||||
if ContactlessCommandRunner.__nfc_wrapper is None:
|
||||
ContactlessCommandRunner.__nfc_wrapper = NfcWrapper()
|
||||
return ContactlessCommandRunner.__nfc_wrapper
|
||||
|
||||
__nfc_wrapper = None
|
||||
|
||||
80
TEST_APPS/testcases/nfc/nfc_cli_helper.py
Normal file
80
TEST_APPS/testcases/nfc/nfc_cli_helper.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
# ice-tea cli commands decorator class
|
||||
|
||||
|
||||
from nfc_messages import NfcErrors
|
||||
import logging
|
||||
import icetea_lib.tools.asserts as asserts
|
||||
|
||||
# Values > 1 k incur large time costs
|
||||
LARGE_BUFFLEN = 400 # Value for large buffer tests, a maximum value can be read from the target with a command
|
||||
|
||||
class CliHelper():
|
||||
"""
|
||||
Helper methods, checks the nfc SDK error-code for you, makes writing a negative test much easier
|
||||
Example:
|
||||
if (target_is_eeprom):
|
||||
nfc_command("dev1", "start", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported)
|
||||
else:
|
||||
nfc_command("dev1", "start")
|
||||
"""
|
||||
|
||||
def nfc_command(self, k, cmd, # pylint: disable=invalid-name
|
||||
wait=True,
|
||||
timeout=10,
|
||||
expected_retcode=0,
|
||||
asynchronous=False,
|
||||
report_cmd_fail=True,
|
||||
expected_nfc_error=NfcErrors.nfc_ok):
|
||||
"""
|
||||
By default will assert if the NFC result code is non-zero.
|
||||
"""
|
||||
response = self.command(k, cmd, wait, timeout, expected_retcode, asynchronous, report_cmd_fail)
|
||||
asserts.assertEqual(int(response.parsed['lastnfcerror']), expected_nfc_error.value)
|
||||
return response
|
||||
|
||||
@staticmethod
|
||||
def command_is(string, command):
|
||||
return string.split(' ')[0] == command
|
||||
|
||||
@staticmethod
|
||||
def debug_nfc_data(key, value):
|
||||
"""
|
||||
print useful data values for the host/user with a >> preamble to make it easy to spot
|
||||
"""
|
||||
text = ">> %s=%s" % (key, value)
|
||||
logging.Logger.info(text)
|
||||
|
||||
def assert_binary_equal(self, left, right):
|
||||
asserts.assertEqual(len(left), len(right), "Buffers are not same length %d %d" % (len(left), len(right)))
|
||||
i = 0
|
||||
while i < len(left):
|
||||
asserts.assertEqual(left[i], ord(right[i]), ("Missmatch @offset %d 0x%x <> 0x%x" % (i, left[i], ord(right[i]))) )
|
||||
i = i + 1
|
||||
|
||||
def assert_text_equal(self, left, right):
|
||||
"""
|
||||
Asserts if the 2 buffers (Text) differ
|
||||
"""
|
||||
asserts.assertEqual(len(left), len(right), "Buffers are not same length %d %d" % (len(left), len(right)))
|
||||
i = 0
|
||||
while i < len(left):
|
||||
asserts.assertEqual(ord(left[i]), ord(right[i]), ("Missmatch @offset %d %d <> %d" % (i, ord(left[i]), ord(right[i]))) )
|
||||
i = i + 1
|
||||
|
||||
89
TEST_APPS/testcases/nfc/nfc_messages.py
Normal file
89
TEST_APPS/testcases/nfc/nfc_messages.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import nfc
|
||||
from enum import Enum
|
||||
import logging
|
||||
|
||||
|
||||
class NfcErrors(Enum):
|
||||
nfc_ok = 0
|
||||
nfc_err_unknown = 1
|
||||
nfc_err_length = 2
|
||||
nfc_err_not_found = 3
|
||||
nfc_err_unsupported = 4
|
||||
nfc_err_params = 5
|
||||
nfc_err_buffer_too_small = 6
|
||||
nfc_err_timeout = 7
|
||||
nfc_err_crc = 8
|
||||
nfc_err_nopeer = 9
|
||||
nfc_err_parity = 10
|
||||
nfc_err_field = 11
|
||||
nfc_err_collision = 12
|
||||
nfc_err_wrong_comm = 13
|
||||
nfc_err_protocol = 14
|
||||
nfc_err_busy = 15
|
||||
nfc_err_controller = 16
|
||||
nfc_err_halted = 17
|
||||
nfc_err_mac = 18
|
||||
nfc_err_underflow = 19
|
||||
nfc_err_disconnected = 20
|
||||
nfc_err_aborted = 21
|
||||
|
||||
|
||||
'''
|
||||
return a 'T'ext text ndef record
|
||||
'''
|
||||
def make_textrecord(text, language='en-US'):
|
||||
return nfc.ndef.Message(nfc.ndef.TextRecord(text, language))
|
||||
|
||||
'''
|
||||
Return an NDEF message
|
||||
resource -- url
|
||||
titles -- list of : colon delimited titles where an optional language code precedes the title -
|
||||
if lang codes are omitted, 'en' is assumed
|
||||
action -- one of default/save/exec/edit
|
||||
'''
|
||||
def make_smartposter(resource, titles, action = 'default'):
|
||||
record = nfc.ndef.SmartPosterRecord(resource)
|
||||
for title in titles:
|
||||
lang, text = title.split(':', 1) if ':' in title else ('en', title)
|
||||
record.title[lang] = text
|
||||
if not action in ('default', 'exec', 'save', 'edit'):
|
||||
logging.error("action not one of 'default', 'exec', 'save', 'edit'")
|
||||
return
|
||||
record.action = action
|
||||
|
||||
return nfc.ndef.Message(record)
|
||||
|
||||
|
||||
'''
|
||||
Program the provided NDEF messsage into the tag (authentication is not required)
|
||||
'''
|
||||
def program_remote_tag(message, tag):
|
||||
if not tag.ndef.is_writeable:
|
||||
logging.error("This Tag is not writeable.")
|
||||
return False
|
||||
tag.ndef.message = message
|
||||
logging.info("Programmed tag OK.")
|
||||
return True
|
||||
|
||||
'''
|
||||
Builds a long string by repeating a shorter string up to the required length
|
||||
'''
|
||||
def repeat_string_to_length(string_to_expand, length):
|
||||
return (string_to_expand * ((length/len(string_to_expand))+1))[:length]
|
||||
259
TEST_APPS/testcases/nfc/test_nfc.py
Normal file
259
TEST_APPS/testcases/nfc/test_nfc.py
Normal file
@@ -0,0 +1,259 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
|
||||
from icetea_lib.bench import Bench
|
||||
from nfc_clf_wrapper import ContactlessCommandRunner
|
||||
import nfc_messages
|
||||
import time
|
||||
from mbed_clitest.tools.tools import test_case
|
||||
import icetea_lib.tools.asserts as asserts
|
||||
from nfc_messages import NfcErrors
|
||||
from nfc_cli_helper import CliHelper
|
||||
from nfc_cli_helper import LARGE_BUFFLEN
|
||||
|
||||
|
||||
class CreamSconeTests(Bench, CliHelper):
|
||||
"""
|
||||
This test wrapper requires a usb connected contactless card reader dongle, and allows E2E testing.
|
||||
See readme file for details
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
testcase_args = {
|
||||
'title':"NFC tests with a reader",
|
||||
'status':"development",
|
||||
'purpose':"NFC e2e",
|
||||
'component':["NFC"],
|
||||
'type':"regression",
|
||||
'requirements':{
|
||||
"duts": {
|
||||
'*': {
|
||||
"count": 1,
|
||||
"type": "hardware",
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-nfcapp"
|
||||
}
|
||||
},
|
||||
"1": {"nick": "dev1"}
|
||||
}
|
||||
}
|
||||
}
|
||||
testcase_args.update(kwargs)
|
||||
Bench.__init__(self, **testcase_args)
|
||||
|
||||
def setup(self):
|
||||
self.logger.info("Test setup: Open Reader and mute...")
|
||||
try:
|
||||
self.clf = ContactlessCommandRunner()
|
||||
self.clf.nfc.mute() # mute if the last test case did not mute
|
||||
except:
|
||||
raise asserts.TestStepFail("Could not find NFC reader")
|
||||
|
||||
def teardown(self):
|
||||
self.logger.info("Test teardown: Reboot target...")
|
||||
self.reset_dut()
|
||||
self.clf.nfc.mute() # mute if the last test case did not mute
|
||||
|
||||
def prepare_target(self):
|
||||
"""
|
||||
simple set up a clean target device
|
||||
:return:
|
||||
"""
|
||||
response = self.nfc_command("dev1", "iseeprom")
|
||||
eeprom = response.parsed['iseeprom']
|
||||
self.logger.info("Target includes NFCEEPROM: %s" % eeprom)
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
if not eeprom:
|
||||
self.nfc_command("dev1", "start")
|
||||
|
||||
self.nfc_command("dev1", "erase")
|
||||
|
||||
|
||||
@test_case(CreamSconeTests)
|
||||
def test_nfce2e_target_found(self):
|
||||
"""
|
||||
smoke - Our emulated tag is detectable
|
||||
"""
|
||||
|
||||
response = self.nfc_command("dev1", "iseeprom")
|
||||
eeprom = response.parsed['iseeprom']
|
||||
|
||||
# Invokes icetea command, this method also checks the NFC api result expected_nfc_error=NFC_OK
|
||||
# Tester can supply the expected_nfc_error=NFC_XXX parameter, to override.
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
if not eeprom:
|
||||
self.nfc_command("dev1", "start")
|
||||
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNotNone(tag, "Could not connect to any tag")
|
||||
|
||||
|
||||
@test_case(CreamSconeTests)
|
||||
def test_nfce2e_smartposter(self):
|
||||
"""
|
||||
check - Tag can be set and read via contactless
|
||||
"""
|
||||
expectedURI = "https://www.mbed.com" # ensure that these differ per test case
|
||||
|
||||
self.prepare_target()
|
||||
|
||||
# write poster tag to target
|
||||
self.command("dev1", "setsmartposter %s" % expectedURI)
|
||||
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNotNone(tag, "Could not connect to any tag")
|
||||
asserts.assertEqual(1, len(tag.ndef.records), "expected number NDEF records")
|
||||
|
||||
asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "SmartposterRecord", "expected SmartposterRecord")
|
||||
asserts.assertEqual(expectedURI, tag.ndef.records[0].uri_records[0].uri, "expected exact URI")
|
||||
|
||||
|
||||
@test_case(CreamSconeTests)
|
||||
def test_nfce2e_reprogrammed(self):
|
||||
"""
|
||||
check - Tag can be programmed from a remote and read via contactless
|
||||
"""
|
||||
expectedURI = "https://www.google.com"
|
||||
|
||||
self.prepare_target()
|
||||
|
||||
# program a poster tag to target
|
||||
self.logger.info("Write Smartposter MESSAGE wirelessly")
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNotNone(tag, "Could not connect to any tag")
|
||||
smartposter = nfc_messages.make_smartposter(expectedURI, ["en-US:Other search engines exist"])
|
||||
nfc_messages.program_remote_tag(smartposter, tag)
|
||||
self.logger.info("Remote programmed %d bytes Smartposter" % len(str(smartposter)))
|
||||
|
||||
self.logger.info("Write back Smartposter MESSAGE wirelessly")
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNotNone(tag, "Could not re-connect to any tag")
|
||||
|
||||
asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "SmartposterRecord", "expected SmartposterRecord")
|
||||
asserts.assertEqual(expectedURI, tag.ndef.records[0].uri_records[0].uri, "expected exact URI")
|
||||
self.clf.nfc.mute() # disable radio, to allow a local session
|
||||
|
||||
# verify in target
|
||||
response = self.nfc_command("dev1", "readmessage")
|
||||
|
||||
# check contents
|
||||
expected_message = str(smartposter)
|
||||
self.assert_binary_equal(response.parsed['nfcmessage'], expected_message)
|
||||
|
||||
|
||||
@test_case(CreamSconeTests)
|
||||
def test_nfce2e_read_stress(self):
|
||||
"""
|
||||
check - Large record can be programmed in and read via contactless
|
||||
"""
|
||||
message_to_repeat = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written
|
||||
text_length = LARGE_BUFFLEN
|
||||
# calculate actual message to compare to using the library
|
||||
expected_text = nfc_messages.repeat_string_to_length(message_to_repeat, text_length)
|
||||
|
||||
self.prepare_target()
|
||||
# write a large message to the tag via API, then read it wirelessly
|
||||
self.logger.info("Write/set tag MESSAGE (%d) bytes" % text_length)
|
||||
self.nfc_command("dev1", "writelong %d %s" % (text_length,message_to_repeat))
|
||||
|
||||
|
||||
# assert that read the eeprom contents gives correct data and length
|
||||
self.logger.info("Read tag MESSAGE wirelessly" )
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNotNone(tag, "Could not connect to any tag")
|
||||
|
||||
asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "TextRecord", "expected TextRecord")
|
||||
self.assert_text_equal(tag.ndef.records[0].text, expected_text)
|
||||
|
||||
|
||||
@test_case(CreamSconeTests)
|
||||
def test_nfce2e_reprogrammed_stress(self):
|
||||
"""
|
||||
check - Large record can be programmed from a remote and read via contactless
|
||||
"""
|
||||
message_to_repeat = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written
|
||||
text_length = LARGE_BUFFLEN # large values slow down test runs and may time out
|
||||
|
||||
# calculate actual message to compare to using the library
|
||||
message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(message_to_repeat, text_length))
|
||||
expected_message = str(message)
|
||||
|
||||
self.prepare_target()
|
||||
# program a large tag to target remotely
|
||||
self.logger.info("Write tag MESSAGE wirelessly (%d) bytes" % len(str(message)))
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNotNone(tag, "Could not connect to any tag")
|
||||
nfc_messages.program_remote_tag(message, tag)
|
||||
self.logger.info("%d bytes chunk of data written to tag remotely" % len(str(message)))
|
||||
self.clf.nfc.mute()
|
||||
|
||||
# read device locally
|
||||
self.logger.info("Read back tag MESSAGE wirelessly")
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNotNone(tag, "Could not re-connect to any tag")
|
||||
asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "TextRecord", "expected TextRecord")
|
||||
self.clf.nfc.mute() # disable the reader radio, to allow local access
|
||||
|
||||
# verify in target
|
||||
response = self.nfc_command("dev1", "readmessage")
|
||||
self.assert_binary_equal(response.parsed['nfcmessage'], expected_message)
|
||||
|
||||
|
||||
@test_case(CreamSconeTests)
|
||||
def test_nfce2e_discovery_loop(self):
|
||||
"""
|
||||
check - Controller discovery loop stop/start
|
||||
fails : blocked by an issue on NFC controllers only
|
||||
"""
|
||||
expectedURI = "https://www.nasa.com" # ensure that these differ per test case
|
||||
|
||||
response = self.command("dev1", "iseeprom") # will hold result from the init lib call
|
||||
eeprom = response.parsed['iseeprom']
|
||||
self.logger.info("Target includes NFCEEPROM: %s" % eeprom)
|
||||
|
||||
self.nfc_command("dev1", "initnfc") # this NOT automatically start discovery at the same time, the test command
|
||||
# "start" must be used on a controller. (Eeeproms always have the loop enabled.)
|
||||
# By default, the test app automatically starts discovery loop again after a reader disconnects from the controller.
|
||||
# Automatic resume after disconnect can be turned off by using command "start man" , the default is "start auto" .
|
||||
|
||||
if not eeprom:
|
||||
# we are muted at this point, and the target is not in discovery mode yet.
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNone(tag, "post-init: Tag discovery loop should be stopped!")
|
||||
self.nfc_command("dev1", "stop")
|
||||
time.sleep(1)
|
||||
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNone(tag, "post-stop: Tag discovery loop should be stopped!")
|
||||
self.nfc_command("dev1", "start")
|
||||
time.sleep(1)
|
||||
|
||||
tag = self.clf.nfc.connect()
|
||||
asserts.assertNotNone(tag, "Could not connect to any tag")
|
||||
|
||||
self.clf.nfc.mute()
|
||||
self.nfc_command("dev1", "stop")
|
||||
time.sleep(10)
|
||||
tag = self.clf.nfc.connect()
|
||||
# test blocked by issue raised IOTPAN313 NFC Controller discovery can stop but cannot restart - PN512
|
||||
asserts.assertNone(tag, "post-restart: Tag discovery loop should be stopped!")
|
||||
|
||||
else:
|
||||
# eeprom, so not supported
|
||||
self.nfc_command("dev1", "start", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported )
|
||||
self.nfc_command("dev1", "stop", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported )
|
||||
|
||||
256
TEST_APPS/testcases/nfc/test_self.py
Normal file
256
TEST_APPS/testcases/nfc/test_self.py
Normal file
@@ -0,0 +1,256 @@
|
||||
"""
|
||||
Copyright (c) 2017, Arm Limited and affiliates.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import time
|
||||
from icetea_lib.bench import Bench
|
||||
from mbed_clitest.tools.tools import test_case
|
||||
import icetea_lib.tools.asserts as asserts
|
||||
import nfc_messages
|
||||
from nfc_messages import NfcErrors
|
||||
from nfc_cli_helper import CliHelper
|
||||
from nfc_cli_helper import LARGE_BUFFLEN
|
||||
import nfc
|
||||
|
||||
"""
|
||||
Standalone (no NFC reader needed) tests, which cover API with no end-to-end checks.
|
||||
"""
|
||||
class CreamSconeSelfTests(Bench, CliHelper):
|
||||
def __init__(self, **kwargs):
|
||||
testcase_args = {
|
||||
'title':"NFC tests with no reader",
|
||||
'status':"development",
|
||||
'purpose':"NFC target-only checks",
|
||||
'component':["NFC"],
|
||||
'type':"smoke",
|
||||
'requirements':{
|
||||
"duts": {
|
||||
'*': {
|
||||
"count": 1,
|
||||
"type": "hardware",
|
||||
"application": {
|
||||
"name": "TEST_APPS-device-nfcapp"
|
||||
}
|
||||
},
|
||||
"1": {"nick": "dev1"}
|
||||
}
|
||||
}
|
||||
}
|
||||
testcase_args.update(kwargs)
|
||||
Bench.__init__(self, **testcase_args)
|
||||
|
||||
def setup(self):
|
||||
pass
|
||||
|
||||
def teardown(self):
|
||||
self.logger.info("Test teardown: Reboot target...")
|
||||
self.reset_dut()
|
||||
|
||||
|
||||
"""
|
||||
smoke - target app is running, and can exchange simple values
|
||||
"""
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_error_codes(self):
|
||||
wally = NfcErrors.nfc_err_not_found
|
||||
for x in range(0, 3):
|
||||
self.nfc_command("dev1", "setlastnfcerror %d" % wally.value, expected_nfc_error=wally)
|
||||
self.nfc_command("dev1", "getlastnfcerror", expected_nfc_error=wally)
|
||||
|
||||
self.nfc_command("dev1", "setlastnfcerror %d" % 0)
|
||||
self.nfc_command("dev1", "getlastnfcerror")
|
||||
|
||||
|
||||
"""
|
||||
smoke - target app reports if NFC eeprom driver present
|
||||
"""
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_eeprom(self):
|
||||
|
||||
response = self.command("dev1", "iseeprom") # will hold result from the init lib call
|
||||
self.logger.info("Target includes NFCEEPROM: %s" % response.parsed['iseeprom'])
|
||||
|
||||
"""
|
||||
check - Assert discovery can be started/stopped
|
||||
"""
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_discovery(self):
|
||||
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
|
||||
response = self.nfc_command("dev1", "iseeprom") # will hold result from the init lib call
|
||||
eeprom = response.parsed['iseeprom']
|
||||
self.logger.info("Target includes NFCEEPROM: %s" % eeprom)
|
||||
if not eeprom:
|
||||
self.nfc_command("dev1", "start")
|
||||
self.nfc_command("dev1", "stop")
|
||||
self.nfc_command("dev1", "start")
|
||||
self.nfc_command("dev1", "stop")
|
||||
else:
|
||||
# eeprom, so not supported
|
||||
self.nfc_command("dev1", "start", expected_retcode=-2, expected_nfc_error = NfcErrors.nfc_err_unsupported )
|
||||
self.nfc_command("dev1", "stop", expected_retcode=-2 , expected_nfc_error= NfcErrors.nfc_err_unsupported )
|
||||
|
||||
"""
|
||||
check - Create a SmartPoster but does not read it back
|
||||
"""
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_setsmartposter(self):
|
||||
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
|
||||
self.nfc_command("dev1", "setsmartposter https://www.mbed.com")
|
||||
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_erase(self):
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
response = self.nfc_command("dev1", "iseeprom")
|
||||
eeprom = response.parsed['iseeprom']
|
||||
if eeprom:
|
||||
self.logger.info("Target includes NFCEEPROM: %s" % eeprom)
|
||||
self.nfc_command("dev1", "erase", timeout=30)
|
||||
response = self.nfc_command("dev1", "readmessage")
|
||||
asserts.assertEqual(response.parsed['nfcmessage'] is None, True)
|
||||
|
||||
'''
|
||||
check - Build a long message by copying a string to stress the driver with a nominal buffer. Verify contents of entire message
|
||||
can be read back.
|
||||
'''
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_write_long(self):
|
||||
messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written
|
||||
textLength = LARGE_BUFFLEN # large values take longer
|
||||
# calculate actual message to compare to using the library
|
||||
message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength))
|
||||
expected_message = str(message)
|
||||
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
response = self.nfc_command("dev1", "iseeprom")
|
||||
eeprom = response.parsed['iseeprom']
|
||||
if eeprom:
|
||||
self.logger.info("Target includes NFCEEPROM: %s" % eeprom)
|
||||
self.nfc_command("dev1", "erase")
|
||||
self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep))
|
||||
response = self.nfc_command("dev1", "readmessage")
|
||||
# assert that read the eeprom contents gives textlength bytes (including framing bytes which will vary)
|
||||
self.assert_binary_equal(response.parsed['nfcmessage'], expected_message)
|
||||
|
||||
'''
|
||||
check - Query supported protocols if we have a controller
|
||||
'''
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_get_controller_protocols(self):
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
|
||||
response = self.nfc_command("dev1", "iseeprom")
|
||||
eeprom = response.parsed['iseeprom']
|
||||
if eeprom:
|
||||
self.logger.info("Test ignore - target includes NFCEEPROM: %s" % eeprom)
|
||||
else:
|
||||
response = self.nfc_command("dev1", "getprotocols")
|
||||
self.logger.info("Protocols = %s" % response.parsed['protocols'])
|
||||
self.assertNotEqual(len(response.parsed['protocols']), 0, "Expected at least 1 protocol supported")
|
||||
|
||||
|
||||
'''
|
||||
check - Can set used protocols if we have a controller
|
||||
Note: Currently only support Typ4 tags in PN512 driver
|
||||
'''
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_set_controller_protocols(self):
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
|
||||
response = self.nfc_command("dev1", "iseeprom")
|
||||
eeprom = response.parsed['iseeprom']
|
||||
if eeprom:
|
||||
# eeproms do not allow target control
|
||||
self.logger.info("Test ignore - target includes NFCEEPROM: %s" % eeprom)
|
||||
else:
|
||||
self.nfc_command("dev1", "setprotocols t1t")
|
||||
self.nfc_command("dev1", "setprotocols t2t")
|
||||
self.nfc_command("dev1", "setprotocols t3t")
|
||||
self.nfc_command("dev1", "setprotocols isodep")
|
||||
self.nfc_command("dev1", "setprotocols nfcdep")
|
||||
self.nfc_command("dev1", "setprotocols t5t")
|
||||
self.nfc_command("dev1", "setprotocols t1t t2t t3t isodep nfcdep t5t")
|
||||
|
||||
'''
|
||||
check - SmartPoster URI forms are supported (in the test-app)
|
||||
'''
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_check_smartposter_uri_forms(self):
|
||||
def enum(**enums):
|
||||
return type('Enum', (), enums)
|
||||
|
||||
IDS = enum(NA=0x00, # Not applicable
|
||||
HTTP_WWW=0x01, # http://www.
|
||||
HTTPS_WWW=0x02, # https://www.
|
||||
HTTP=0x03, # http://
|
||||
HTTPS=0x04, # https://
|
||||
TEL=0x05, # tel:
|
||||
MAILTO=0x06, # mailto:
|
||||
FTP_ANONYMOUS=0x07, # ftp://anonymous:anonymous@
|
||||
FTP_FTP=0x08, # ftp://ftp.
|
||||
FTPS=0x09, # ftps://
|
||||
SFTP=0x0A, # sftp://
|
||||
SMB=0x0B, # smb://
|
||||
NFS=0x0C, # nfs://
|
||||
FTP=0x0D, # ftp://
|
||||
DAV=0x0E, # dav://
|
||||
NEWS=0x0F, # news:
|
||||
TELNET=0x10, # telnet://
|
||||
IMAP=0x11, # imap:
|
||||
RSTP=0x12, # rstp://
|
||||
URN=0x13, # urn:
|
||||
POP=0x14, # pop:
|
||||
SIP=0x15, # sip:
|
||||
SIPS=0x16, # sips:
|
||||
TFTP=0x17, # tftp:
|
||||
BTSPP=0x18, # btspp://
|
||||
BTL2CAP=0x19, # btl2cap://
|
||||
BTGOEP=0x1A, # btgoep://
|
||||
TCPOBEX=0x1B, # tcpobex://
|
||||
IRDAOBEX=0x1C, # irdaobex://
|
||||
FILE=0x1D, # file://
|
||||
URN_EPC_ID=0x1E, # urn:epc:id:
|
||||
URN_EPC_TAG=0x1F, # urn:epc:tag:
|
||||
URN_EPC_PAT=0x20, # urn:epc:pat:
|
||||
URN_EPC_RAW=0x21, # urn:epc:raw:
|
||||
URN_EPC=0x22, # urn:epc:
|
||||
URN_NFC=0x23, # urn:nfc:
|
||||
)
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
result = self.nfc_command("dev1", "setsmartposter https://www.mbed.com")
|
||||
asserts.assertEqual(result.parsed['uri_id'], IDS.HTTPS_WWW, "uri type expected HTTPS_WWW")
|
||||
result = self.nfc_command("dev1", "setsmartposter http://www.mbed.com")
|
||||
asserts.assertEqual(result.parsed['uri_id'], IDS.HTTP_WWW)
|
||||
result = self.nfc_command("dev1", "setsmartposter https://www.topleveldomain")
|
||||
asserts.assertEqual(result.parsed['uri_id'], IDS.HTTPS_WWW)
|
||||
result = self.nfc_command("dev1", "setsmartposter tel:555-5551234")
|
||||
asserts.assertEqual(result.parsed['uri_id'], IDS.TEL)
|
||||
result = self.nfc_command("dev1", "setsmartposter ftp://www.mbed.com/files/")
|
||||
asserts.assertEqual(result.parsed['uri_id'], IDS.FTP )
|
||||
|
||||
'''
|
||||
smoke - driver buffer size can be retrieved
|
||||
'''
|
||||
@test_case(CreamSconeSelfTests)
|
||||
def test_nfc_get_max_ndef(self):
|
||||
self.nfc_command("dev1", "initnfc")
|
||||
max = self.nfc_command("dev1", "getmaxndef").parsed['maxndef']
|
||||
self.logger.info("Target NDEF max buffer size %d" % max)
|
||||
self.logger.info("Teststress size %d" % LARGE_BUFFLEN)
|
||||
|
||||
Reference in New Issue
Block a user