Preserve main GNSS and volume behavior while porting verified TLS, receive fixes and full-handshake optimizations. Add MQTT connection diagnostics and route MQTT through banban.api.talkingq.com:1883. Include migration documentation and regression tools. Firmware builds and package checks passed; user confirmed new-board connectivity after switching the MQTT host to the domain.
108 lines
4.8 KiB
Python
108 lines
4.8 KiB
Python
"""Exercise actual MQTT diagnostic callbacks without a broker or credentials."""
|
|
from pathlib import Path
|
|
import subprocess
|
|
|
|
root = Path(__file__).resolve().parents[1]
|
|
output = root / 'artifacts/wss/new-board/mqtt-diag/tests'
|
|
output.mkdir(parents=True, exist_ok=True)
|
|
(output / 'MQTTClient.h').write_text(r'''
|
|
#ifndef TEST_MQTT_CLIENT_H
|
|
#define TEST_MQTT_CLIENT_H
|
|
typedef struct Network Network;
|
|
struct Network {
|
|
int (*mqttread)(Network *, unsigned char *, int, int);
|
|
int (*mqttwrite)(Network *, unsigned char *, int, int);
|
|
};
|
|
typedef struct { Network *ipstack; unsigned int command_timeout_ms; int isconnected; } MQTTClient;
|
|
typedef struct { unsigned char MQTTVersion, cleansession; unsigned short keepAliveInterval; } MQTTPacket_connectData;
|
|
typedef struct { unsigned char rc, sessionPresent; } MQTTConnackData;
|
|
int MQTTConnectWithResults(MQTTClient *, MQTTPacket_connectData *, MQTTConnackData *);
|
|
unsigned int ql_rtos_get_systicks(void);
|
|
#endif
|
|
''')
|
|
(output / 'sockets.h').write_text('int ql_get_soc_errno(void);\n')
|
|
harness = r'''
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "mqtt_connect_diag.h"
|
|
static unsigned int ticks;
|
|
static int mode, offset, writes, reads;
|
|
unsigned int ql_rtos_get_systicks(void) { return ticks; }
|
|
int ql_get_soc_errno(void) { return 104; }
|
|
static int fake_write(Network *n, unsigned char *buf, int size, int timeout) {
|
|
(void)n;
|
|
assert(timeout == 30000 && size == 19);
|
|
assert(memcmp(buf, "PRIVATE_TEST_SECRET", 19) == 0);
|
|
++writes; ticks += 2;
|
|
return mode == 2 ? -1 : size;
|
|
}
|
|
static int fake_read(Network *n, unsigned char *buf, int size, int timeout) {
|
|
unsigned char response[] = {0x20, 2, 0, 0};
|
|
(void)n;
|
|
assert(timeout == 30000 && (size == 1 || size == 2));
|
|
++reads; ticks += 3;
|
|
if (mode == 3) return -1;
|
|
if (mode == 4) return 0;
|
|
if (mode == 1) response[3] = 5;
|
|
if (mode == 5) response[0] = 0x30;
|
|
if (mode == 7) { response[2] = 1; response[3] = 5; }
|
|
memcpy(buf, response + offset, size); offset += size;
|
|
return size;
|
|
}
|
|
int MQTTConnectWithResults(MQTTClient *client, MQTTPacket_connectData *options, MQTTConnackData *ack) {
|
|
unsigned char packet[] = "PRIVATE_TEST_SECRET";
|
|
unsigned char response[4] = {0};
|
|
int result;
|
|
assert(options->MQTTVersion == 3);
|
|
assert(ack->rc == 255);
|
|
if (mode == 6) return -1;
|
|
result = client->ipstack->mqttwrite(client->ipstack, packet, 19, 30000);
|
|
if (result != 19) { assert(result == -1); return -1; }
|
|
/* SDK reads fixed header, remaining length, then the two-byte body. */
|
|
result = client->ipstack->mqttread(client->ipstack, response, 1, 30000);
|
|
if (mode == 3 || mode == 4) { assert(result == (mode == 3 ? -1 : 0)); return -1; }
|
|
assert(result == 1);
|
|
assert(client->ipstack->mqttread(client->ipstack, response + 1, 1, 30000) == 1);
|
|
assert(client->ipstack->mqttread(client->ipstack, response + 2, 2, 30000) == 2);
|
|
assert(response[1] == 2);
|
|
return mode == 0 ? 0 : (mode == 1 ? 5 : -1);
|
|
}
|
|
int main(void) {
|
|
Network network = {fake_read, fake_write};
|
|
MQTTClient client = {&network, 30000, 0};
|
|
MQTTPacket_connectData options = {3, 0, 60};
|
|
for (mode = 0; mode < 8; ++mode) {
|
|
int expected = mode == 0 ? 0 : (mode == 1 ? 5 : -1);
|
|
ticks = mode == 0 ? 0xfffffffeU : 0; offset = writes = reads = 0;
|
|
printf("CASE %d\n", mode);
|
|
assert(talkingq_mqtt_connect_diagnosed(&client, &options) == expected);
|
|
assert(network.mqttread == fake_read && network.mqttwrite == fake_write);
|
|
assert(writes == (mode == 6 ? 0 : 1));
|
|
if (mode == 6 || mode == 2) assert(reads == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
(output / 'test.c').write_text(harness)
|
|
include = root / 'ql-application/threadx/interface/custom_app/inc'
|
|
source = root / 'ql-application/threadx/interface/custom_app/mqtt_connect_diag.c'
|
|
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/TC', '/I' + str(output), '/I' + str(include),
|
|
str(source), str(output / 'test.c'), '/Fe' + str(output / 'test.exe')], cwd=output, check=True)
|
|
result = subprocess.check_output([str(output / 'test.exe')], text=True)
|
|
assert 'PRIVATE_TEST_SECRET' not in result
|
|
cases = [part for part in result.split('CASE ')[1:]]
|
|
assert len(cases) == 8
|
|
assert 'CONNACK code=0 reason=accepted' in cases[0] and 'elapsed_ms=55' in cases[0]
|
|
assert 'CONNACK code=5 reason=not_authorized' in cases[1]
|
|
assert 'write request=19 rc=-1' in cases[2]
|
|
assert 'read request=1 rc=-1' in cases[3]
|
|
assert 'read request=1 rc=0' in cases[4]
|
|
assert 'no valid initial CONNACK' in cases[5]
|
|
assert 'failed before transport write' in cases[6]
|
|
assert 'no valid initial CONNACK' in cases[7]
|
|
for case in cases[2:]:
|
|
assert '[MQTT-DIAG] CONNACK code=' not in case
|
|
(output / 'test-output.log').write_text(result)
|
|
print('PASS: transport arguments/return values, callback restoration, split CONNACK, rejection, invalid response, errors, no-I/O failure, tick wrap and credential redaction')
|