1""" 2Various LwM2M interoperability tests 3#################################### 4 5Copyright (c) 2023 Nordic Semiconductor ASA 6 7SPDX-License-Identifier: Apache-2.0 8 9Test specification: 10=================== 11https://www.openmobilealliance.org/release/LightweightM2M/ETS/OMA-ETS-LightweightM2M-V1_1-20190912-D.pdf 12 13 14This module contains testcases for 15 * Registration Interface [100-199] 16 * Device management & Service Enablement Interface [200-299] 17 * Information Reporting Interface [300-399] 18 19""" 20 21import time 22import logging 23from datetime import datetime 24import pytest 25from leshan import Leshan 26 27from twister_harness import Shell 28from twister_harness import DeviceAdapter 29 30logger = logging.getLogger(__name__) 31 32 33def test_LightweightM2M_1_1_int_102(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 34 """LightweightM2M-1.1-int-102 - Registration Update""" 35 lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/1 -u32')) 36 lifetime = int(lines[0]) 37 lifetime = lifetime + 10 38 start_time = time.time() * 1000 39 leshan.write(endpoint, '1/0/1', lifetime) 40 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) 41 latest = leshan.get(f'/clients/{endpoint}') 42 assert latest["lastUpdate"] > start_time 43 assert latest["lastUpdate"] <= time.time()*1000 44 assert latest["lifetime"] == lifetime 45 shell.exec_command('lwm2m write 1/0/1 -u32 86400') 46 47def test_LightweightM2M_1_1_int_103(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 48 """LightweightM2M-1.1-int-103 - Deregistration""" 49 leshan.execute(endpoint, '1/0/4') 50 dut.readlines_until(regex='LwM2M server disabled', timeout=5.0) 51 dut.readlines_until(regex='Deregistration success', timeout=5.0) 52 # Reset timers by restarting the client 53 shell.exec_command('lwm2m stop') 54 time.sleep(1) 55 shell.exec_command(f'lwm2m start {endpoint}') 56 dut.readlines_until(regex='.*Registration Done', timeout=5.0) 57 58def test_LightweightM2M_1_1_int_104(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 59 """LightweightM2M-1.1-int-104 - Registration Update Trigger""" 60 shell.exec_command('lwm2m update') 61 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) 62 leshan.execute(endpoint, '1/0/8') 63 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) 64 65@pytest.mark.slow 66def test_LightweightM2M_1_1_int_107(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 67 """LightweightM2M-1.1-int-107 - Extending the lifetime of a registration""" 68 leshan.write(endpoint, '1/0/1', 120) 69 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) 70 lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/1 -u32')) 71 lifetime = int(lines[0]) 72 assert lifetime == 120 73 logger.debug(f'Wait for update, max {lifetime} s') 74 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=lifetime) 75 assert leshan.get(f'/clients/{endpoint}') 76 77def test_LightweightM2M_1_1_int_108(leshan, endpoint): 78 """LightweightM2M-1.1-int-108 - Turn on Queue Mode""" 79 assert leshan.get(f'/clients/{endpoint}')["queuemode"] 80 81@pytest.mark.slow 82def test_LightweightM2M_1_1_int_109(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 83 """LightweightM2M-1.1-int-109 - Behavior in Queue Mode""" 84 logger.debug('Wait for Queue RX OFF') 85 dut.readlines_until(regex='.*Queue mode RX window closed', timeout=120) 86 # Restore previous value 87 shell.exec_command('lwm2m write 1/0/1 -u32 86400') 88 dut.readlines_until(regex='.*Registration update complete', timeout=10) 89 90def test_LightweightM2M_1_1_int_201(shell: Shell, leshan: Leshan, endpoint: str): 91 """LightweightM2M-1.1-int-201 - Querying basic information in Plain Text format""" 92 fmt = leshan.format 93 leshan.format = 'TEXT' 94 assert leshan.read(endpoint, '3/0/0') == 'Zephyr' 95 assert leshan.read(endpoint, '3/0/1') == 'client-1' 96 assert leshan.read(endpoint, '3/0/2') == 'serial-1' 97 leshan.format = fmt 98 99def verify_device_object(resp): 100 ''' Verify that Device object match Configuration 3 ''' 101 assert resp[0][0] == 'Zephyr' 102 assert resp[0][1] == 'client-1' 103 assert resp[0][2] == 'serial-1' 104 assert resp[0][3] == '1.2.3' 105 assert resp[0][11][0] == 0 106 assert resp[0][16] == 'U' 107 108def verify_server_object(obj): 109 ''' Verify that server object match Configuration 3 ''' 110 assert obj[0][0] == 1 111 assert obj[0][1] == 86400 112 assert obj[0][2] == 1 113 assert obj[0][3] == 10 114 assert obj[0][5] == 86400 115 assert obj[0][6] is False 116 assert obj[0][7] == 'U' 117 118def test_LightweightM2M_1_1_int_203(shell: Shell, leshan: Leshan, endpoint: str): 119 """LightweightM2M-1.1-int-203 - Querying basic information in TLV format""" 120 fmt = leshan.format 121 leshan.format = 'TLV' 122 resp = leshan.read(endpoint,'3/0') 123 verify_device_object(resp) 124 leshan.format = fmt 125 126def test_LightweightM2M_1_1_int_204(shell: Shell, leshan: Leshan, endpoint: str): 127 """LightweightM2M-1.1-int-204 - Querying basic information in JSON format""" 128 fmt = leshan.format 129 leshan.format = 'JSON' 130 resp = leshan.read(endpoint, '3/0') 131 verify_device_object(resp) 132 leshan.format = fmt 133 134def test_LightweightM2M_1_1_int_205(shell: Shell, leshan: Leshan, endpoint: str): 135 """LightweightM2M-1.1-int-205 - Setting basic information in Plain Text format""" 136 fmt = leshan.format 137 leshan.format = 'TEXT' 138 leshan.write(endpoint, '1/0/2', 101) 139 leshan.write(endpoint, '1/0/3', 1010) 140 leshan.write(endpoint, '1/0/5', 2000) 141 assert leshan.read(endpoint, '1/0/2') == 101 142 assert leshan.read(endpoint, '1/0/3') == 1010 143 assert leshan.read(endpoint, '1/0/5') == 2000 144 leshan.write(endpoint, '1/0/2', 1) 145 leshan.write(endpoint, '1/0/3', 10) 146 leshan.write(endpoint, '1/0/5', 86400) 147 assert leshan.read(endpoint, '1/0/2') == 1 148 assert leshan.read(endpoint, '1/0/3') == 10 149 assert leshan.read(endpoint, '1/0/5') == 86400 150 leshan.format = fmt 151 152def test_LightweightM2M_1_1_int_211(shell: Shell, leshan: Leshan, endpoint: str): 153 """LightweightM2M-1.1-int-211 - Querying basic information in CBOR format""" 154 fmt = leshan.format 155 leshan.format = 'CBOR' 156 lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/0 -u16')) 157 short_id = int(lines[0]) 158 assert leshan.read(endpoint, '1/0/0') == short_id 159 assert leshan.read(endpoint, '1/0/6') is False 160 assert leshan.read(endpoint, '1/0/7') == 'U' 161 leshan.format = fmt 162 163def test_LightweightM2M_1_1_int_212(shell: Shell, leshan: Leshan, endpoint: str): 164 """LightweightM2M-1.1-int-212 - Setting basic information in CBOR format""" 165 fmt = leshan.format 166 leshan.format = 'CBOR' 167 leshan.write(endpoint, '1/0/2', 101) 168 leshan.write(endpoint, '1/0/3', 1010) 169 leshan.write(endpoint, '1/0/6', True) 170 assert leshan.read(endpoint, '1/0/2') == 101 171 assert leshan.read(endpoint, '1/0/3') == 1010 172 assert leshan.read(endpoint, '1/0/6') is True 173 leshan.write(endpoint, '1/0/2', 1) 174 leshan.write(endpoint, '1/0/3', 10) 175 leshan.write(endpoint, '1/0/6', False) 176 leshan.format = fmt 177 178def verify_setting_basic_in_format(shell, leshan, endpoint, format): 179 fmt = leshan.format 180 leshan.format = format 181 server_obj = leshan.read(endpoint, '1/0') 182 verify_server_object(server_obj) 183 # Remove Read-Only resources, so we don't end up writing those 184 del server_obj[0][0] 185 del server_obj[0][13] 186 data = { 187 2: 101, 188 3: 1010, 189 5: 2000, 190 6: True, 191 7: 'U' 192 } 193 assert leshan.update_obj_instance(endpoint, '1/0', data)['status'] == 'CHANGED(204)' 194 resp = leshan.read(endpoint, '1/0') 195 assert resp[0][2] == 101 196 assert resp[0][3] == 1010 197 assert resp[0][5] == 2000 198 assert resp[0][6] is True 199 assert resp[0][7] == 'U' 200 assert leshan.replace_obj_instance(endpoint, '1/0', server_obj[0])['status'] == 'CHANGED(204)' 201 server_obj = leshan.read(endpoint, '1/0') 202 verify_server_object(server_obj) 203 leshan.format = fmt 204 205def test_LightweightM2M_1_1_int_215(shell: Shell, leshan: Leshan, endpoint: str): 206 """LightweightM2M-1.1-int-215 - Setting basic information in TLV format""" 207 verify_setting_basic_in_format(shell, leshan, endpoint, 'TLV') 208 209def test_LightweightM2M_1_1_int_220(shell: Shell, leshan: Leshan, endpoint: str): 210 """LightweightM2M-1.1-int-220 - Setting basic information in JSON format""" 211 verify_setting_basic_in_format(shell, leshan, endpoint, 'JSON') 212 213def test_LightweightM2M_1_1_int_221(shell: Shell, leshan: Leshan, endpoint: str): 214 """LightweightM2M-1.1-int-221 - Attempt to perform operations on Security""" 215 assert leshan.read(endpoint, '0/0')['status'] == 'UNAUTHORIZED(401)' 216 assert leshan.write(endpoint, '0/0/0', 'coap://localhost')['status'] == 'UNAUTHORIZED(401)' 217 assert leshan.write_attributes(endpoint, '0', {'pmin':10})['status'] == 'UNAUTHORIZED(401)' 218 219def test_LightweightM2M_1_1_int_222(shell: Shell, leshan: Leshan, endpoint: str): 220 """LightweightM2M-1.1-int-222 - Read on Object""" 221 resp = leshan.read(endpoint, '1') 222 assert len(resp) == 1 223 assert len(resp[1][0]) == 11 224 resp = leshan.read(endpoint, '3') 225 assert len(resp) == 1 226 assert len(resp[3]) == 1 227 assert len(resp[3][0]) == 15 228 assert resp[3][0][0] == 'Zephyr' 229 230def test_LightweightM2M_1_1_int_223(shell: Shell, leshan: Leshan, endpoint: str): 231 """LightweightM2M-1.1-int-223 - Read on Object Instance""" 232 resp = leshan.read(endpoint, '1/0') 233 assert len(resp[0]) == 11 234 resp = leshan.read(endpoint, '3/0') 235 assert len(resp[0]) == 15 236 assert resp[0][0] == 'Zephyr' 237 238def test_LightweightM2M_1_1_int_224(shell: Shell, leshan: Leshan, endpoint: str): 239 """LightweightM2M-1.1-int-224 - Read on Resource""" 240 assert leshan.read(endpoint, '1/0/0') == 1 241 assert leshan.read(endpoint, '1/0/1') == 86400 242 assert leshan.read(endpoint, '1/0/6') is False 243 assert leshan.read(endpoint, '1/0/7') == 'U' 244 245def test_LightweightM2M_1_1_int_225(shell: Shell, leshan: Leshan, endpoint: str): 246 """LightweightM2M-1.1-int-225 - Read on Resource Instance""" 247 assert leshan.read(endpoint, '3/0/11/0') == 0 248 249def test_LightweightM2M_1_1_int_226(shell: Shell, leshan: Leshan, endpoint: str): 250 """LightweightM2M-1.1-int-226 - Write (Partial Update) on Object Instance""" 251 lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/1 -u32')) 252 lifetime = int(lines[0]) 253 resources = { 254 1: 60, 255 6: True 256 } 257 assert leshan.update_obj_instance(endpoint, '1/0', resources)['status'] == 'CHANGED(204)' 258 assert leshan.read(endpoint, '1/0/1') == 60 259 assert leshan.read(endpoint, '1/0/6') is True 260 resources = { 261 1: lifetime, 262 6: False 263 } 264 assert leshan.update_obj_instance(endpoint, '1/0', resources)['status'] == 'CHANGED(204)' 265 266def test_LightweightM2M_1_1_int_227(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 267 """LightweightM2M-1.1-int-227 - Write (replace) on Resource""" 268 lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/1 -u32')) 269 lifetime = int(lines[0]) 270 assert leshan.write(endpoint, '1/0/1', int(63))['status'] == 'CHANGED(204)' 271 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) 272 latest = leshan.get(f'/clients/{endpoint}') 273 assert latest["lifetime"] == 63 274 assert leshan.read(endpoint, '1/0/1') == 63 275 assert leshan.write(endpoint, '1/0/1', lifetime)['status'] == 'CHANGED(204)' 276 277def test_LightweightM2M_1_1_int_228(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 278 """LightweightM2M-1.1-int-228 - Write on Resource Instance""" 279 resources = { 280 0: {0: 'a', 1: 'b'} 281 } 282 assert leshan.create_obj_instance(endpoint, '16/0', resources)['status'] == 'CREATED(201)' 283 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) 284 assert leshan.write(endpoint, '16/0/0/0', 'test')['status'] == 'CHANGED(204)' 285 assert leshan.read(endpoint, '16/0/0/0') == 'test' 286 287def test_LightweightM2M_1_1_int_229(shell: Shell, leshan: Leshan, endpoint: str): 288 """LightweightM2M-1.1-int-229 - Read-Composite Operation""" 289 old_fmt = leshan.format 290 for fmt in ['SENML_JSON', 'SENML_CBOR']: 291 leshan.format = fmt 292 resp = leshan.composite_read(endpoint, ['/3', '1/0']) 293 assert len(resp.keys()) == 2 294 assert resp[3] is not None 295 assert resp[1][0] is not None 296 assert len(resp[3][0]) == 15 297 assert len(resp[1][0]) == 11 298 299 resp = leshan.composite_read(endpoint, ['1/0/1', '/3/0/11/0']) 300 logger.debug(resp) 301 assert len(resp.keys()) == 2 302 assert resp[1][0][1] is not None 303 assert resp[3][0][11][0] is not None 304 leshan.format = old_fmt 305 306def test_LightweightM2M_1_1_int_230(shell: Shell, leshan: Leshan, endpoint: str): 307 """LightweightM2M-1.1-int-230 - Write-Composite Operation""" 308 resources = { 309 "/1/0/1": 60, 310 "/1/0/6": True, 311 "/16/0/0": { 312 "0": "aa", 313 "1": "bb", 314 "2": "cc", 315 "3": "dd" 316 } 317 } 318 old_fmt = leshan.format 319 for fmt in ['SENML_JSON', 'SENML_CBOR']: 320 leshan.format = fmt 321 assert leshan.composite_write(endpoint, resources)['status'] == 'CHANGED(204)' 322 resp = leshan.read(endpoint, '1/0') 323 assert resp[0][1] == 60 324 assert resp[0][6] is True 325 resp = leshan.read(endpoint, '16/0/0') 326 assert resp[0][0] == "aa" 327 assert resp[0][1] == "bb" 328 assert resp[0][2] == "cc" 329 assert resp[0][3] == "dd" 330 # Return to default 331 shell.exec_command('lwm2m write /1/0/1 -u32 86400') 332 shell.exec_command('lwm2m write /1/0/6 -u8 0') 333 leshan.format = old_fmt 334 335def query_basic_in_senml(leshan: Leshan, endpoint: str, fmt: str): 336 """Querying basic information in one of the SenML formats""" 337 old_fmt = leshan.format 338 leshan.format = fmt 339 verify_server_object(leshan.read(endpoint, '1')[1]) 340 verify_device_object(leshan.read(endpoint, '3/0')) 341 assert leshan.read(endpoint, '3/0/16') == 'U' 342 assert leshan.read(endpoint, '3/0/11/0') == 0 343 leshan.format = old_fmt 344 345def test_LightweightM2M_1_1_int_231(shell: Shell, leshan: Leshan, endpoint: str): 346 """LightweightM2M-1.1-int-231 - Querying basic information in SenML JSON format""" 347 query_basic_in_senml(leshan, endpoint, 'SENML_JSON') 348 349def test_LightweightM2M_1_1_int_232(shell: Shell, leshan: Leshan, endpoint: str): 350 """LightweightM2M-1.1-int-232 - Querying basic information in SenML CBOR format""" 351 query_basic_in_senml(leshan, endpoint, 'SENML_CBOR') 352 353def setting_basic_senml(shell: Shell, leshan: Leshan, endpoint: str, fmt: str): 354 """Setting basic information in one of the SenML formats""" 355 old_fmt = leshan.format 356 leshan.format = fmt 357 resources = { 358 1: 61, 359 6: True, 360 } 361 assert leshan.update_obj_instance(endpoint, '1/0', resources)['status'] == 'CHANGED(204)' 362 srv_obj = leshan.read(endpoint, '1/0') 363 assert srv_obj[0][1] == 61 364 assert srv_obj[0][6] is True 365 assert leshan.write(endpoint, '16/0/0/0', 'test_value')['status'] == 'CHANGED(204)' 366 portfolio = leshan.read(endpoint, '16') 367 assert portfolio[16][0][0][0] == 'test_value' 368 assert leshan.write(endpoint, '1/0/1', 63)['status'] == 'CHANGED(204)' 369 assert leshan.read(endpoint, '1/0/1') == 63 370 shell.exec_command('lwm2m write /1/0/1 -u32 86400') 371 shell.exec_command('lwm2m write /1/0/6 -u8 0') 372 leshan.format = old_fmt 373 374def test_LightweightM2M_1_1_int_233(shell: Shell, leshan: Leshan, endpoint: str): 375 """LightweightM2M-1.1-int-233 - Setting basic information in SenML CBOR format""" 376 setting_basic_senml(shell, leshan, endpoint, 'SENML_CBOR') 377 378def test_LightweightM2M_1_1_int_234(shell: Shell, leshan: Leshan, endpoint: str): 379 """LightweightM2M-1.1-int-234 - Setting basic information in SenML JSON format""" 380 setting_basic_senml(shell, leshan, endpoint, 'SENML_JSON') 381 382def test_LightweightM2M_1_1_int_235(leshan: Leshan, endpoint: str): 383 """LightweightM2M-1.1-int-235 - Read-Composite Operation on root path""" 384 resp = leshan.composite_read(endpoint, ['/']) 385 expected_keys = [1, 3, 5] 386 missing_keys = [key for key in expected_keys if key not in resp.keys()] 387 assert len(missing_keys) == 0 388 389def test_LightweightM2M_1_1_int_236(shell: Shell, leshan: Leshan, endpoint: str): 390 """LightweightM2M-1.1-int-236 - Read-Composite - Partial Presence""" 391 resp = leshan.composite_read(endpoint, ['1/0', '/3/0/11/0', '/3339/0/5522', '/3353/0/6030']) 392 assert resp[1][0][1] is not None 393 assert resp[3][0][11][0] is not None 394 assert len(resp) == 2 395 396def test_LightweightM2M_1_1_int_237(shell: Shell, leshan: Leshan, endpoint: str): 397 """LightweightM2M-1.1-int-237 - Read on Object without specifying Content-Type""" 398 old_fmt = leshan.format 399 leshan.format = None 400 assert leshan.read(endpoint, '1')[1][0][1] is not None 401 assert leshan.read(endpoint, '3')[3][0][0] == 'Zephyr' 402 leshan.format = old_fmt 403 404def test_LightweightM2M_1_1_int_241(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 405 """LightweightM2M-1.1-int-241 - Executable Resource: Rebooting the device""" 406 leshan.execute(endpoint, '3/0/4') 407 dut.readlines_until(regex='.*DEVICE: REBOOT', timeout=5.0) 408 dut.readlines_until(regex='.*rd_client_event: Disconnected', timeout=5.0) 409 shell.exec_command(f'lwm2m start {endpoint} -b 0') 410 dut.readlines_until(regex='.*Registration Done', timeout=5.0) 411 assert leshan.get(f'/clients/{endpoint}') 412 413def test_LightweightM2M_1_1_int_256(shell: Shell, leshan: Leshan, endpoint: str): 414 """LightweightM2M-1.1-int-256 - Write Operation Failure""" 415 lines = shell.get_filtered_output(shell.exec_command('lwm2m read 1/0/0 -u16')) 416 short_id = int(lines[0]) 417 assert leshan.write(endpoint, '1/0/0', 123)['status'] == 'METHOD_NOT_ALLOWED(405)' 418 assert leshan.read(endpoint, '1/0/0') == short_id 419 420def test_LightweightM2M_1_1_int_257(shell: Shell, leshan: Leshan, endpoint: str): 421 """LightweightM2M-1.1-int-257 - Write-Composite Operation""" 422 resources = { 423 "/1/0/2": 102, 424 "/1/0/6": True, 425 "/3/0/13": datetime.fromtimestamp(0) 426 } 427 old_fmt = leshan.format 428 for fmt in ['SENML_JSON', 'SENML_CBOR']: 429 leshan.format = fmt 430 assert leshan.composite_write(endpoint, resources)['status'] == 'CHANGED(204)' 431 assert leshan.read(endpoint, '1/0/2') == 102 432 assert leshan.read(endpoint, '1/0/6') is True 433 # Cannot verify the /3/0/13, it is a timestamp that moves forward. 434 435 # Return to default 436 shell.exec_command(f'lwm2m write /3/0/13 -u32 {int(datetime.now().timestamp())}') 437 shell.exec_command('lwm2m write /1/0/6 -u8 0') 438 shell.exec_command('lwm2m write /1/0/2 -u32 1') 439 leshan.format = old_fmt 440 441def test_LightweightM2M_1_1_int_260(shell: Shell, leshan: Leshan, endpoint: str): 442 """LightweightM2M-1.1-int-260 - Discover Command""" 443 resp = leshan.discover(endpoint, '3') 444 expected_keys = ['/3', '/3/0', '/3/0/1', '/3/0/2', '/3/0/3', '/3/0/4', '/3/0/6', '/3/0/7', '/3/0/8', '/3/0/9', '/3/0/11', '/3/0/16'] 445 missing_keys = [key for key in expected_keys if key not in resp.keys()] 446 assert len(missing_keys) == 0 447 assert leshan.write_attributes(endpoint, '3', {'pmin': 10, 'pmax': 200})['status'] == 'CHANGED(204)' 448 resp = leshan.discover(endpoint, '3/0') 449 assert int(resp['/3/0/6']['dim']) == 2 450 assert int(resp['/3/0/7']['dim']) == 2 451 assert int(resp['/3/0/8']['dim']) == 2 452 assert leshan.write_attributes(endpoint, '3/0/7', {'lt': 1, 'gt': 6, 'st': 1})['status'] == 'CHANGED(204)' 453 resp = leshan.discover(endpoint, '3/0') 454 expected_keys = ['/3/0', '/3/0/1', '/3/0/2', '/3/0/3', '/3/0/4', '/3/0/6', '/3/0/7', '/3/0/8', '/3/0/9', '/3/0/11', '/3/0/16'] 455 missing_keys = [key for key in expected_keys if key not in resp.keys()] 456 assert len(missing_keys) == 0 457 assert int(resp['/3/0/7']['dim']) == 2 458 assert float(resp['/3/0/7']['lt']) == 1.0 459 assert float(resp['/3/0/7']['gt']) == 6.0 460 assert float(resp['/3/0/7']['st']) == 1.0 461 resp = leshan.discover(endpoint, '3/0/7') 462 expected_keys = ['/3/0/7', '/3/0/7/0', '/3/0/7/1'] 463 missing_keys = [key for key in expected_keys if key not in resp.keys()] 464 assert len(missing_keys) == 0 465 assert len(resp) == len(expected_keys) 466 # restore 467 leshan.remove_attributes(endpoint, '3', ['pmin', 'pmax']) 468 469def test_LightweightM2M_1_1_int_261(shell: Shell, leshan: Leshan, endpoint: str): 470 """LightweightM2M-1.1-int-261 - Write-Attribute Operation on a multiple resource""" 471 resp = leshan.discover(endpoint, '3/0/11') 472 logger.debug(resp) 473 expected_keys = ['/3/0/11', '/3/0/11/0'] 474 missing_keys = [key for key in expected_keys if key not in resp.keys()] 475 assert len(missing_keys) == 0 476 assert len(resp) == len(expected_keys) 477 assert int(resp['/3/0/11']['dim']) == 1 478 assert leshan.write_attributes(endpoint, '3', {'pmin':10, 'pmax':200})['status'] == 'CHANGED(204)' 479 assert leshan.write_attributes(endpoint, '3/0', {'pmax':320})['status'] == 'CHANGED(204)' 480 assert leshan.write_attributes(endpoint, '3/0/11/0', {'pmax':100, 'epmin':1, 'epmax':20})['status'] == 'CHANGED(204)' 481 resp = leshan.discover(endpoint, '3/0/11') 482 logger.debug(resp) 483 assert int(resp['/3/0/11']['pmin']) == 10 484 assert int(resp['/3/0/11']['pmax']) == 320 485 assert int(resp['/3/0/11/0']['pmax']) == 100 486 # Note: Zephyr does not support epmin&epmax. 487 # Restore 488 leshan.remove_attributes(endpoint, '3', ['pmin', 'pmax']) 489 leshan.remove_attributes(endpoint, '3/0', ['pmax']) 490 leshan.remove_attributes(endpoint, '3/0/11/0', ['pmax']) 491 492 493def test_LightweightM2M_1_1_int_280(shell: Shell, leshan: Leshan, endpoint: str): 494 """LightweightM2M-1.1-int-280 - Successful Read-Composite Operation""" 495 resp = leshan.composite_read(endpoint, ['/3/0/16', '/3/0/11/0', '/1/0']) 496 logger.debug(resp) 497 assert len(resp) == 2 498 assert len(resp[3]) == 1 499 assert len(resp[3][0]) == 2 # No extra resources 500 assert resp[3][0][11][0] == 0 501 assert resp[3][0][16] == 'U' 502 assert resp[1][0][0] == 1 503 assert resp[1][0][1] == 86400 504 assert resp[1][0][6] is False 505 assert resp[1][0][7] == 'U' 506 507def test_LightweightM2M_1_1_int_281(shell: Shell, leshan: Leshan, endpoint: str): 508 """LightweightM2M-1.1-int-281 - Partially Successful Read-Composite Operation""" 509 resp = leshan.composite_read(endpoint, ['/1/0/1', '/1/0/7', '/1/0/8']) 510 assert len(resp) == 1 511 assert len(resp[1][0]) == 2 # /1/0/8 should not be there 512 assert resp[1][0][1] == 86400 513 assert resp[1][0][7] == 'U' 514 515# 516# Information Reporting Interface [300-399] 517# 518 519@pytest.mark.slow 520def test_LightweightM2M_1_1_int_301(shell: Shell, leshan: Leshan, endpoint: str): 521 """LightweightM2M-1.1-int-301 - Observation and Notification of parameter values""" 522 pwr_src = leshan.read(endpoint, '3/0/6') 523 logger.debug(pwr_src) 524 assert pwr_src[6][0] == 1 525 assert pwr_src[6][1] == 5 526 assert leshan.write_attributes(endpoint, '3/0/7', {'pmin': 5, 'pmax': 10})['status'] == 'CHANGED(204)' 527 leshan.observe(endpoint, '3/0/7') 528 with leshan.get_event_stream(endpoint, timeout=30) as events: 529 shell.exec_command('lwm2m write /3/0/7/0 -u32 3000') 530 data = events.next_event('NOTIFICATION') 531 assert data is not None 532 assert data[3][0][7][0] == 3000 533 # Ensure that we don't get new data before pMin 534 start = time.time() 535 shell.exec_command('lwm2m write /3/0/7/0 -u32 3500') 536 data = events.next_event('NOTIFICATION') 537 assert data[3][0][7][0] == 3500 538 assert (start + 5) < time.time() + 0.5 # Allow 0.5 second diff 539 assert (start + 5) > time.time() - 0.5 540 # Ensure that we get update when pMax expires 541 data = events.next_event('NOTIFICATION') 542 assert data[3][0][7][0] == 3500 543 assert (start + 15) <= time.time() + 1 # Allow 1 second slack. (pMinx + pMax=15) 544 leshan.cancel_observe(endpoint, '3/0/7') 545 leshan.remove_attributes(endpoint, '3/0/7', ['pmin', 'pmax']) 546 547def test_LightweightM2M_1_1_int_302(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 548 """LightweightM2M-1.1-int-302 - Cancel Observations using Reset Operation""" 549 leshan.observe(endpoint, '3/0/7') 550 leshan.observe(endpoint, '3/0/8') 551 with leshan.get_event_stream(endpoint) as events: 552 shell.exec_command('lwm2m write /3/0/7/0 -u32 4000') 553 data = events.next_event('NOTIFICATION') 554 assert data[3][0][7][0] == 4000 555 leshan.passive_cancel_observe(endpoint, '3/0/7') 556 shell.exec_command('lwm2m write /3/0/7/0 -u32 3000') 557 dut.readlines_until(regex=r'.*Observer removed for 3/0/7') 558 with leshan.get_event_stream(endpoint) as events: 559 shell.exec_command('lwm2m write /3/0/8/0 -u32 100') 560 data = events.next_event('NOTIFICATION') 561 assert data[3][0][8][0] == 100 562 leshan.passive_cancel_observe(endpoint, '3/0/8') 563 shell.exec_command('lwm2m write /3/0/8/0 -u32 50') 564 dut.readlines_until(regex=r'.*Observer removed for 3/0/8') 565 566def test_LightweightM2M_1_1_int_303(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 567 """LightweightM2M-1.1-int-303 - Cancel observations using Observe with Cancel parameter""" 568 leshan.observe(endpoint, '3/0/7') 569 leshan.observe(endpoint, '3/0/8') 570 with leshan.get_event_stream(endpoint) as events: 571 shell.exec_command('lwm2m write /3/0/7/0 -u32 4000') 572 data = events.next_event('NOTIFICATION') 573 assert data[3][0][7][0] == 4000 574 leshan.cancel_observe(endpoint, '3/0/7') 575 dut.readlines_until(regex=r'.*Observer removed for 3/0/7') 576 with leshan.get_event_stream(endpoint) as events: 577 shell.exec_command('lwm2m write /3/0/8/0 -u32 100') 578 data = events.next_event('NOTIFICATION') 579 assert data[3][0][8][0] == 100 580 leshan.cancel_observe(endpoint, '3/0/8') 581 dut.readlines_until(regex=r'.*Observer removed for 3/0/8') 582 583@pytest.mark.slow 584def test_LightweightM2M_1_1_int_304(shell: Shell, leshan: Leshan, endpoint: str): 585 """LightweightM2M-1.1-int-304 - Observe-Composite Operation""" 586 # Need to use Configuration C.1 587 shell.exec_command('lwm2m write 1/0/2 -u32 0') 588 shell.exec_command('lwm2m write 1/0/3 -u32 0') 589 assert leshan.write_attributes(endpoint, '1/0/1', {'pmin': 30, 'pmax': 45})['status'] == 'CHANGED(204)' 590 data = leshan.composite_observe(endpoint, ['/1/0/1', '/3/0/11/0', '/3/0/16']) 591 assert data[1][0][1] is not None 592 assert data[3][0][11][0] is not None 593 assert data[3][0][16] == 'U' 594 assert len(data) == 2 595 assert len(data[1]) == 1 596 assert len(data[3][0]) == 2 597 start = time.time() 598 with leshan.get_event_stream(endpoint, timeout=50) as events: 599 data = events.next_event('NOTIFICATION') 600 logger.debug(data) 601 assert data[1][0][1] is not None 602 assert data[3][0][11][0] is not None 603 assert data[3][0][16] == 'U' 604 assert len(data) == 2 605 assert len(data[1]) == 1 606 assert len(data[3][0]) == 2 607 assert (start + 30) < time.time() 608 assert (start + 45) > time.time() - 1 609 leshan.cancel_composite_observe(endpoint, ['/1/0/1', '/3/0/11/0', '/3/0/16']) 610 # Restore configuration C.3 611 shell.exec_command('lwm2m write 1/0/2 -u32 1') 612 shell.exec_command('lwm2m write 1/0/3 -u32 10') 613 leshan.remove_attributes(endpoint, '1/0/1', ['pmin', 'pmax']) 614 615def test_LightweightM2M_1_1_int_305(dut: DeviceAdapter, leshan: Leshan, endpoint: str): 616 """LightweightM2M-1.1-int-305 - Cancel Observation-Composite Operation""" 617 leshan.composite_observe(endpoint, ['/1/0/1', '/3/0/11/0', '/3/0/16']) 618 leshan.cancel_composite_observe(endpoint, ['/1/0/1', '/3/0/11/0', '/3/0/16']) 619 dut.readlines_until(regex=r'.*Observer removed for 1/0/1') 620 dut.readlines_until(regex=r'.*Observer removed for 3/0/11/0') 621 dut.readlines_until(regex=r'.*Observer removed for 3/0/16') 622 623def test_LightweightM2M_1_1_int_306(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 624 """LightweightM2M-1.1-int-306 - Send Operation""" 625 with leshan.get_event_stream(endpoint) as events: 626 shell.exec_command('lwm2m send /1 /3') 627 dut.readlines_until(regex=r'.*SEND status: 0', timeout=5.0) 628 data = events.next_event('SEND') 629 assert data is not None 630 verify_server_object(data[1]) 631 verify_device_object(data[3]) 632 633def test_LightweightM2M_1_1_int_307(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 634 """LightweightM2M-1.1-int-307 - Muting Send""" 635 leshan.write(endpoint, '1/0/23', True) 636 lines = shell.get_filtered_output(shell.exec_command('lwm2m send /3/0')) 637 assert any("can't do send operation" in line for line in lines) 638 leshan.write(endpoint, '1/0/23', False) 639 shell.exec_command('lwm2m send /3/0') 640 dut.readlines_until(regex=r'.*SEND status: 0', timeout=5.0) 641 642 643@pytest.mark.slow 644def test_LightweightM2M_1_1_int_308(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 645 """LightweightM2M-1.1-int-308 - Observe-Composite and Creating Object Instance""" 646 shell.exec_command('lwm2m delete /16/0') 647 shell.exec_command('lwm2m delete /16/1') 648 # Need to use Configuration C.1 649 shell.exec_command('lwm2m write 1/0/2 -u32 0') 650 shell.exec_command('lwm2m write 1/0/3 -u32 0') 651 resources_a = { 652 0: {0: 'aa', 653 1: 'bb', 654 2: 'cc', 655 3: 'dd'} 656 } 657 content_one = {16: {0: resources_a}} 658 resources_b = { 659 0: {0: '11', 660 1: '22', 661 2: '33', 662 3: '44'} 663 } 664 content_both = {16: {0: resources_a, 1: resources_b}} 665 assert leshan.create_obj_instance(endpoint, '16/0', resources_a)['status'] == 'CREATED(201)' 666 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) 667 assert leshan.write_attributes(endpoint, '16/0', {'pmin': 30, 'pmax': 45})['status'] == 'CHANGED(204)' 668 data = leshan.composite_observe(endpoint, ['/16/0', '/16/1']) 669 assert data == content_one 670 with leshan.get_event_stream(endpoint, timeout=50) as events: 671 data = events.next_event('NOTIFICATION') 672 start = time.time() 673 assert data == content_one 674 assert leshan.create_obj_instance(endpoint, '16/1', resources_b)['status'] == 'CREATED(201)' 675 data = events.next_event('NOTIFICATION') 676 assert (start + 30) < time.time() + 2 677 assert (start + 45) > time.time() - 2 678 assert data == content_both 679 leshan.cancel_composite_observe(endpoint, ['/16/0', '/16/1']) 680 # Restore configuration C.3 681 shell.exec_command('lwm2m write 1/0/2 -u32 1') 682 shell.exec_command('lwm2m write 1/0/3 -u32 10') 683 leshan.remove_attributes(endpoint, '16/0', ['pmin','pmax']) 684 685@pytest.mark.slow 686def test_LightweightM2M_1_1_int_309(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str): 687 """LightweightM2M-1.1-int-309 - Observe-Composite and Deleting Object Instance""" 688 shell.exec_command('lwm2m delete /16/0') 689 shell.exec_command('lwm2m delete /16/1') 690 # Need to use Configuration C.1 691 shell.exec_command('lwm2m write 1/0/2 -u32 0') 692 shell.exec_command('lwm2m write 1/0/3 -u32 0') 693 resources_a = { 694 0: {0: 'aa', 695 1: 'bb', 696 2: 'cc', 697 3: 'dd'} 698 } 699 content_one = {16: {0: resources_a}} 700 resources_b = { 701 0: {0: '11', 702 1: '22', 703 2: '33', 704 3: '44'} 705 } 706 content_both = {16: {0: resources_a, 1: resources_b}} 707 assert leshan.create_obj_instance(endpoint, '16/0', resources_a)['status'] == 'CREATED(201)' 708 assert leshan.create_obj_instance(endpoint, '16/1', resources_b)['status'] == 'CREATED(201)' 709 dut.readlines_until(regex='.*net_lwm2m_rd_client: Update Done', timeout=5.0) 710 assert leshan.write_attributes(endpoint, '16/0', {'pmin': 30, 'pmax': 45})['status'] == 'CHANGED(204)' 711 data = leshan.composite_observe(endpoint, ['/16/0', '/16/1']) 712 assert data == content_both 713 with leshan.get_event_stream(endpoint, timeout=50) as events: 714 data = events.next_event('NOTIFICATION') 715 start = time.time() 716 assert data == content_both 717 assert leshan.delete(endpoint, '16/1')['status'] == 'DELETED(202)' 718 data = events.next_event('NOTIFICATION') 719 assert (start + 30) < time.time() + 2 720 assert (start + 45) > time.time() - 2 721 assert data == content_one 722 leshan.cancel_composite_observe(endpoint, ['/16/0', '/16/1']) 723 # Restore configuration C.3 724 shell.exec_command('lwm2m write 1/0/2 -u32 1') 725 shell.exec_command('lwm2m write 1/0/3 -u32 10') 726 leshan.remove_attributes(endpoint, '16/0', ['pmin', 'pmax']) 727 728@pytest.mark.slow 729def test_LightweightM2M_1_1_int_310(shell: Shell, leshan: Leshan, endpoint: str): 730 """LightweightM2M-1.1-int-310 - Observe-Composite and modification of parameter values""" 731 # Need to use Configuration C.1 732 shell.exec_command('lwm2m write 1/0/2 -u32 0') 733 shell.exec_command('lwm2m write 1/0/3 -u32 0') 734 leshan.composite_observe(endpoint, ['/1/0/1', '/3/0']) 735 with leshan.get_event_stream(endpoint, timeout=50) as events: 736 assert leshan.write_attributes(endpoint, '3', {'pmax': 5})['status'] == 'CHANGED(204)' 737 start = time.time() 738 data = events.next_event('NOTIFICATION') 739 assert data[3][0][0] == 'Zephyr' 740 assert data[1] == {0: {1: 86400}} 741 assert (start + 5) > time.time() - 1 742 start = time.time() 743 data = events.next_event('NOTIFICATION') 744 assert (start + 5) > time.time() - 1 745 leshan.cancel_composite_observe(endpoint, ['/1/0/1', '/3/0']) 746 # Restore configuration C.3 747 shell.exec_command('lwm2m write 1/0/2 -u32 1') 748 shell.exec_command('lwm2m write 1/0/3 -u32 10') 749 leshan.remove_attributes(endpoint, '3', ['pmax']) 750 751def test_LightweightM2M_1_1_int_311(shell: Shell, leshan: Leshan, endpoint: str): 752 """LightweightM2M-1.1-int-311 - Send command""" 753 with leshan.get_event_stream(endpoint, timeout=50) as events: 754 shell.exec_command('lwm2m send /1/0/1 /3/0/11') 755 data = events.next_event('SEND') 756 assert data == {3: {0: {11: {0: 0}}}, 1: {0: {1: 86400}}} 757