155 lines
10 KiB
JavaScript
155 lines
10 KiB
JavaScript
// Executes the actual device adapter against deterministic transport/NVS/clock
|
|||
|
|
// doubles. These tests are NOT supplier release evidence or hardware acceptance.
|
||
|
|
const fs=require('node:fs'),path=require('node:path');
|
||
|
|
const base=path.join(__dirname,'..');
|
||
|
|
const jsonDir=path.join(process.env.IDF_PATH||'E:/Codex/toolchains/esp-idf-v5.5.1','components/json/cJSON');
|
||
|
|
const strip=s=>s.replace(/^#include[^\n]*$/gm,'').replace(/^#pragma once/gm,'');
|
||
|
|
const sdk=strip(fs.readFileSync(path.join(base,'components/volc_conv_ai/src/base/volc_device_manager.h'),'utf8'));
|
||
|
|
const version=strip(fs.readFileSync(path.join(base,'main/firmware_version.h'),'utf8'));
|
||
|
|
process.stdout.write(fs.readFileSync(path.join(jsonDir,'cJSON.h'),'utf8')+'\n'+fs.readFileSync(path.join(jsonDir,'cJSON.c'),'utf8').replace('#include "cJSON.h"','')+`
|
||
|
|
#include <assert.h>
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdatomic.h>
|
||
|
|
#include <sys/time.h>
|
||
|
|
typedef int esp_err_t;
|
||
|
|
typedef int nvs_handle_t;
|
||
|
|
typedef int StaticSemaphore_t;
|
||
|
|
typedef int SemaphoreHandle_t;
|
||
|
|
#define ESP_OK 0
|
||
|
|
#define ESP_FAIL -1
|
||
|
|
#define ESP_ERR_NO_MEM 2
|
||
|
|
#define ESP_ERR_INVALID_STATE 3
|
||
|
|
#define ESP_ERR_INVALID_RESPONSE 4
|
||
|
|
#define ESP_ERR_INVALID_SIZE 5
|
||
|
|
#define ESP_ERR_TIMEOUT 6
|
||
|
|
#define ESP_ERR_INVALID_ARG 7
|
||
|
|
#define ESP_ERR_NVS_NOT_FOUND 7
|
||
|
|
#define NVS_READWRITE 1
|
||
|
|
#define NVS_READONLY 0
|
||
|
|
#define pdTRUE 1
|
||
|
|
#define pdPASS 1
|
||
|
|
#define portMAX_DELAY 0
|
||
|
|
#define pdMS_TO_TICKS(x) (x)
|
||
|
|
#define ESP_LOGI(...) ((void)0)
|
||
|
|
static int64_t test_us=1000000;
|
||
|
|
static int64_t esp_timer_get_time(void){return test_us;}
|
||
|
|
static int xSemaphoreCreateMutexStatic(void *p){return 1;}
|
||
|
|
static int xSemaphoreTake(int m,int t){return pdTRUE;}
|
||
|
|
static void xSemaphoreGive(int m){}
|
||
|
|
static void vTaskDelay(int t){}
|
||
|
|
static int xTaskCreate(void(*f)(void*),const char*n,int stack,void*a,int priority,void*out){return pdPASS;}
|
||
|
|
static int nvs_open(const char *s,int mode,int *n){*n=1;return 0;}
|
||
|
|
static int credential_writes, storage_error; static unsigned char saved_record[2048]; static size_t saved_len;
|
||
|
|
static int nvs_set_blob(int n,const char *key,const void *data,size_t len){if(!strcmp(key,"credential")){credential_writes++;if(!storage_error){assert(len<=sizeof(saved_record));memcpy(saved_record,data,len);saved_len=len;}}return 0;}
|
||
|
|
static int nvs_get_blob(int n,const char *key,void *data,size_t *len){if(!strcmp(key,"credential")&&saved_len==*len){memcpy(data,saved_record,saved_len);return 0;}return ESP_ERR_NVS_NOT_FOUND;}
|
||
|
|
static int nvs_erase_key(int n,const char *key){return 0;}
|
||
|
|
static int nvs_commit(int n){return storage_error;}
|
||
|
|
static void nvs_close(int n){}
|
||
|
|
static void mbedtls_platform_zeroize(void*p,size_t n){memset(p,0,n);}
|
||
|
|
static void mbedtls_sha256(const unsigned char*p,size_t n,unsigned char*out,int x){memset(out,0,32);for(size_t i=0;i<n;i++)out[i%32]^=p[i];}
|
||
|
|
static bool ble_cloud_configured(void){return true;}
|
||
|
|
static const char*ble_factory_id(void){return "KP01-000000000001";}
|
||
|
|
static const char*ble_factory_model(void){return "ECHOEAR-S3";}
|
||
|
|
static const char*ble_cloud_boot_id(void){return "11111111-1111-4111-8111-111111111111";}
|
||
|
|
static const char*ble_cloud_challenge_id(void){return ble_cloud_boot_id();}
|
||
|
|
static int ble_cloud_sync_time(void){return 0;}
|
||
|
|
static char *test_config;
|
||
|
|
static int config_mode, imported, supplier_calls;
|
||
|
|
static char *ble_cloud_decode(const char*d,const char*e,const char*s){return test_config?strdup(test_config):NULL;}
|
||
|
|
static unsigned uuid_counter;
|
||
|
|
static void ble_random_uuid(char out[37]){snprintf(out,37,"11111111-1111-4111-8111-%012u",++uuid_counter);}
|
||
|
|
`+sdk+'\n'+version+`
|
||
|
|
int volc_device_register(volc_iot_info_t*i,char**o){supplier_calls++;return -1;}
|
||
|
|
`+strip(fs.readFileSync(path.join(base,'main/platform_client.h'),'utf8'))+'\n'+strip(fs.readFileSync(path.join(base,'main/call_client.h'),'utf8'))+`
|
||
|
|
|
||
|
|
static int response_mode, requests;
|
||
|
|
static char *last_body;
|
||
|
|
static char last_id[37];
|
||
|
|
static int64_t request_advance;
|
||
|
|
const char *platform_last_error_code(void) {return response_mode==2 ? "CHAT_ENDED" : "";}
|
||
|
|
esp_err_t platform_request(const char*method,const char*path,const char*body,const char*rid,char**out){
|
||
|
|
requests++; free(last_body);last_body=strdup(body);snprintf(last_id,37,"%s",rid?rid:"");
|
||
|
|
if(response_mode) return ESP_FAIL;
|
||
|
|
cJSON *j=cJSON_Parse(body),*root=cJSON_CreateObject(),*data=cJSON_AddObjectToObject(root,"data");cJSON_AddBoolToObject(root,"ok",true);
|
||
|
|
assert(!strstr(path,"/voice-runtime")&&!strstr(path,"/activation-")&&!strstr(path,"/device/register"));
|
||
|
|
if(strstr(path,"/ble-state"))cJSON_AddBoolToObject(data,"bound",config_mode!=4);
|
||
|
|
if(strstr(path,"/voice-config")){
|
||
|
|
imported++;
|
||
|
|
cJSON_AddStringToObject(data,"voice_status",config_mode==1?"preparing":"ready");
|
||
|
|
cJSON *iot=cJSON_AddObjectToObject(data,"voice");
|
||
|
|
cJSON_AddStringToObject(iot,"instance_id","instance");cJSON_AddStringToObject(iot,"product_key","product");
|
||
|
|
cJSON_AddStringToObject(iot,"device_name",ble_factory_id());cJSON_AddStringToObject(iot,"device_secret","fixture-device-secret-123456789");
|
||
|
|
if(config_mode!=3)cJSON_AddStringToObject(iot,"RTCAppID","rtc-app");
|
||
|
|
}
|
||
|
|
if(strstr(path,"/chat/start")){
|
||
|
|
struct timeval tv;gettimeofday(&tv,NULL);int64_t now=(int64_t)tv.tv_sec*1000+tv.tv_usec/1000+2000;
|
||
|
|
cJSON_AddNumberToObject(root,"server_time_ms",now);
|
||
|
|
cJSON_AddStringToObject(data,"session_id",cJSON_GetStringValue(cJSON_GetObjectItem(j,"session_id")));
|
||
|
|
cJSON_AddStringToObject(data,"bot_id","bot");cJSON_AddObjectToObject(data,"params");
|
||
|
|
cJSON_AddNumberToObject(data,"lease_expires_at_ms",now+45000);cJSON_AddNumberToObject(data,"expires_at_ms",now+3600000);
|
||
|
|
test_us+=request_advance*1000;
|
||
|
|
}
|
||
|
|
if(out)*out=cJSON_PrintUnformatted(root);cJSON_Delete(j);cJSON_Delete(root);return 0;
|
||
|
|
}
|
||
|
|
`+strip(fs.readFileSync(path.join(base,'main/call_client.c'),'utf8'))+`
|
||
|
|
static void reset(void){
|
||
|
|
free(params);params=NULL;memset(&call,0,sizeof(call));call.version=2;
|
||
|
|
snprintf(call.session,37,"%s",ble_cloud_boot_id());snprintf(call.boot,37,"%s",ble_cloud_boot_id());
|
||
|
|
snprintf(call.end_id,37,"%s",ble_cloud_boot_id());test_us=1000000;
|
||
|
|
atomic_store(&audio_allowed,true);atomic_store(&lease_deadline,mono()+45000);
|
||
|
|
response_mode=requests=0;request_advance=0;state_mutex=1;engine_ready=true;sdk_stopped=false;
|
||
|
|
}
|
||
|
|
int main(void){
|
||
|
|
cJSON *bound=cJSON_CreateObject();
|
||
|
|
cJSON_AddStringToObject(bound,"instance_id","instance");cJSON_AddStringToObject(bound,"product_key","product");
|
||
|
|
cJSON_AddStringToObject(bound,"device_name","device");cJSON_AddStringToObject(bound,"device_secret","fixture-device-secret-123456789");
|
||
|
|
assert(call_client_save_bind_voice(bound)==ESP_ERR_INVALID_RESPONSE);assert(credential_writes==0);
|
||
|
|
cJSON_AddStringToObject(bound,"RTCAppID","rtc-app");
|
||
|
|
assert(call_client_save_bind_voice(bound)==ESP_OK);assert(credential_writes==1);
|
||
|
|
storage_error=ESP_FAIL;assert(call_client_save_bind_voice(bound)==ESP_FAIL);storage_error=0;cJSON_Delete(bound);
|
||
|
|
reset();assert(call_client_audio_allowed());test_us+=45000000;assert(!call_client_audio_allowed());
|
||
|
|
reset();assert(volc_platform_recording_prepare("app","room","device")==ESP_OK);
|
||
|
|
assert(strstr(last_body,"rtc_app_id")&&strstr(last_body,"room_id")&&strstr(last_body,"rtc_user_id"));
|
||
|
|
assert(!strstr(last_body,"device_secret")&&!strstr(last_body,"audio"));
|
||
|
|
int recorded_requests=requests;call.finished=true;
|
||
|
|
assert(volc_platform_recording_prepare("app","room","device")==ESP_ERR_INVALID_STATE);assert(requests==recorded_requests);
|
||
|
|
reset();response_mode=1;assert(volc_platform_recording_prepare("app","room","device")!=ESP_OK);assert(call_client_audio_allowed());
|
||
|
|
reset();assert(fetch_parameters(true)==0);assert(params && !strcmp(bot,"bot"));
|
||
|
|
reset();request_advance=46000;assert(fetch_parameters(false)!=0);assert(!call_client_audio_allowed());
|
||
|
|
reset();int before=requests;assert(call_client_turn("subject","1","private text")==0);assert(requests==before);
|
||
|
|
reset();call.finished=true;call.sdk_stopped=true;strcpy(call.reason,"hangup");response_mode=1;
|
||
|
|
finish_pending();assert(call.session[0]);char *original=strdup(last_body);char rid[37];strcpy(rid,last_id);
|
||
|
|
response_mode=0;finish_pending();assert(!call.session[0]);assert(!strcmp(original,last_body)&&!strcmp(rid,last_id));free(original);
|
||
|
|
reset();response_mode=2;renew_due=0;assert(call_client_poll()==1);assert(!call_client_audio_allowed());
|
||
|
|
reset();memset(&call,0,sizeof(call));response_mode=1;assert(call_client_begin()!=0);assert(call.finished&&call.session[0]);
|
||
|
|
response_mode=0;finish_pending();assert(!call.session[0]);
|
||
|
|
reset();memset(&call,0,sizeof(call));memset(&voice,0,sizeof(voice));boot_loaded=false;config_mode=0;imported=0;
|
||
|
|
assert(call_client_boot()==ESP_OK);assert(!strcmp(voice.rtc_app,"rtc-app"));assert(imported==0);assert(supplier_calls==0);
|
||
|
|
before=requests;call_client_engine_ready(true);assert(engine_ready);assert(requests==before);
|
||
|
|
config_mode=4;assert(call_client_boot()==ESP_ERR_INVALID_STATE);assert(!strcmp(call_client_start_error(),"DEVICE_UNBOUND"));assert(!call_client_audio_allowed());
|
||
|
|
config_mode=0;memset(&voice,0,sizeof(voice));saved_len=0;boot_loaded=false;
|
||
|
|
assert(call_client_boot()==ESP_OK);assert(imported==1);assert(supplier_calls==0);
|
||
|
|
memset(&voice,0,sizeof(voice));saved_len=0;boot_loaded=false;config_mode=1;
|
||
|
|
assert(call_client_boot()==ESP_ERR_INVALID_STATE);assert(!strcmp(call_client_start_error(),"VOICE_PREPARING"));
|
||
|
|
config_mode=3;assert(call_client_boot()==ESP_ERR_INVALID_RESPONSE);assert(supplier_calls==0);
|
||
|
|
config_mode=0;assert(call_client_boot()==ESP_OK);
|
||
|
|
// A disruptive operation atomically excludes a new call, including a race
|
||
|
|
// between command delivery and the user's next screen tap.
|
||
|
|
memset(&call,0,sizeof(call));call_client_engine_ready(true);
|
||
|
|
assert(!call_client_busy());assert(call_client_enter_maintenance());
|
||
|
|
assert(call_client_busy());assert(call_client_begin()==ESP_ERR_INVALID_STATE);
|
||
|
|
assert(call_client_set_standby(true)==ESP_OK);
|
||
|
|
call_client_leave_maintenance();assert(!call_client_busy());
|
||
|
|
assert(call_client_standby());assert(call_client_begin()==ESP_ERR_INVALID_STATE);
|
||
|
|
assert(call_client_set_standby(false)==ESP_ERR_INVALID_STATE);
|
||
|
|
assert(call_client_enter_maintenance());assert(call_client_set_standby(false)==ESP_OK);
|
||
|
|
call_client_leave_maintenance();assert(!call_client_standby());
|
||
|
|
strcpy(call.session,"11111111-1111-4111-8111-111111111111");
|
||
|
|
assert(call_client_busy());assert(!call_client_enter_maintenance());
|
||
|
|
free(test_config);test_config=NULL;
|
||
|
|
reset();free(params);free(last_body);puts("PASS direct chat C: bind credential persistence, cached boot, recovery only, no activation handshake, parameter fetch, stable end retry, unbound refusal and lease expiry");return 0;
|
||
|
|
}
|
||
|
|
`);
|