chore: 发布V1.1.26版本,修复Sink角色切换初始化残留问题
1. 更新所有文件版本号至V1.1.26,同步固件版本寄存器值 2. 修复Sink角色往返时初始化上下文残留问题,切换回Sink时重新执行完整初始化 3. 优化Sink配置生效逻辑,同角色仅复制PC可写配置,保留运行状态 4. 增加初始化阶段入队标记,避免沿用旧成功结果跳过未执行命令 5. 停止入队失败时由北向轮询重试,不依赖On_work或通道使能 6. 新增Sink初始化回归测试套件,覆盖14种场景验证流程正确性
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
# Sink 初始化回归验证
|
||||
|
||||
## 主机测试
|
||||
|
||||
在仓库根目录执行(需要 Node.js 和本机 GCC):
|
||||
|
||||
```powershell
|
||||
node tests/sink_init/run.cjs C:/Qt/Tools/mingw1310_64/bin/gcc.exe
|
||||
```
|
||||
|
||||
测试编译真实 `chrg_sink.c`、从 COM 提取的配置生效函数和北向状态调度片段;只模拟时钟、南向批次、继电器及无关外设。每个场景独立进程,临时产物自动清理。
|
||||
|
||||
14 个用例覆盖:旧影子配置保留实时状态、重复退出 Short、初始化中配置变化返回忙、重复 INIT 恢复、初始化中停止、首批/后续阶段入队失败、批次失败重试、完整初始化后再次退出 Short、重复/拒绝请求不改变配置版本、停止入队失败后台重试、Sink 初始化中切换角色后重新初始化。
|
||||
|
||||
主机测试不能验证实际串口回码匹配、线程竞争、继电器时序或功率板执行结果。
|
||||
|
||||
## 硬件验收(尚未执行)
|
||||
|
||||
使用现有 PC 指令和安全负载,先单通道,再四通道同时重复以下场景:
|
||||
|
||||
1. 普通 5V/1A 启停、Short 进入/退出各循环至少 100 次;退出期间重复发送同一参数生效请求。预期退出初始化持续推进,最终可恢复带载。
|
||||
2. 初始化期间修改配置并生效。预期返回 Modbus 忙(异常码 `0x06`),PC 在初始化完成后重试;旧事务参数及北向回码推进不被覆盖。
|
||||
3. 在各初始化阶段发送停止;短时断开南向通信后恢复。预期停止优先、未完成阶段重试,不以旧成功结果提前进入 WAIT。
|
||||
4. 初始化未完成时切 Source,再切回 Sink。预期重新清短路、配置继电器和负载参数,不复用旧初始化阶段。
|
||||
|
||||
用 Keil Watch 观察 `sink.work_mode`、`sink.On_work`、`sink.test.test_mode_old`、`g_sink_init_in_progress`、`g_sink_init_stage`、`g_sink_init_stage_submitted`、`sub` 和 `config_version`;避免高频打印。结合南向回码及实测电流确认执行,不能仅以入队成功作为验收依据。
|
||||
|
||||
本次应用版本为 **V1.1.26**,对外固件版本寄存器 `0x0300` 为 `0x012A`;Bootloader 不变。
|
||||
@@ -0,0 +1,210 @@
|
||||
/* 本文件由 run.cjs 拼接到真实产品函数后。每个场景独立进程,静态状态不会串扰。 */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
struct chrg_north_t chrgnorth;
|
||||
struct chrg_south_t chrgsouth;
|
||||
struct chrg_thread_t chrgthr[TOTAL_THRS];
|
||||
static rt_tick_t now;
|
||||
static int pending, result, fail_submit, cancel_count, submit_count;
|
||||
static int queue_capacity = 1;
|
||||
static rt_uint16_t last_reg, last_val;
|
||||
static rt_uint32_t sample_seq;
|
||||
|
||||
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "line %d: %s\n", __LINE__, #x); exit(1); } } while (0)
|
||||
rt_tick_t rt_tick_get(void) { return now; }
|
||||
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms) { return (rt_tick_t)ms; }
|
||||
int rt_kprintf(const char *format, ...) { (void)format; return 0; }
|
||||
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout)
|
||||
{ (void)mutex; (void)timeout; return 0; }
|
||||
rt_err_t rt_mutex_release(rt_mutex_t mutex) { (void)mutex; return 0; }
|
||||
void chrg_roll_nor_source_switch_reset(rt_uint8_t ch) { (void)ch; CHECK(0); }
|
||||
int chrg_source_work_state(rt_uint8_t ch, struct chrg_switch_t *p)
|
||||
{ (void)ch; (void)p; CHECK(0); return -1; }
|
||||
static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
|
||||
struct chrg_switch_t *p, const struct chrg_switch_t *s)
|
||||
{ (void)ch; (void)p; (void)s; CHECK(0); return MB_EX_SLAVE_BUSY; }
|
||||
int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs,
|
||||
const rt_uint16_t *vals, rt_uint8_t count)
|
||||
{
|
||||
(void)ch;
|
||||
CHECK(count > 0);
|
||||
if (fail_submit) { fail_submit--; return -1; }
|
||||
CHECK(pending < queue_capacity); /* 角色切换允许连续两批,其余场景禁止重复提交。 */
|
||||
submit_count++;
|
||||
last_reg = regs[0]; last_val = vals[0];
|
||||
pending++; result = 0;
|
||||
return 0;
|
||||
}
|
||||
int chrg_sou_com_batch_cancel(eIDX_SOU_CH ch)
|
||||
{ (void)ch; cancel_count++; pending = 0; result = -1; return 0; }
|
||||
rt_bool_t chrg_sou_com_batch_is_pending(eIDX_SOU_CH ch)
|
||||
{ (void)ch; return pending; }
|
||||
int chrg_sou_com_batch_get_result(eIDX_SOU_CH ch)
|
||||
{ (void)ch; return result; }
|
||||
int chrg_sou_measure_once_submit(eIDX_SOU_CH ch) { (void)ch; return 0; }
|
||||
rt_uint32_t chrg_eload_get_sample_seq(rt_uint8_t ch) { (void)ch; return sample_seq; }
|
||||
void chrg_nor_sw_rel(eIDX_NOR_CH ch, eIDX_ID id) { (void)ch; (void)id; }
|
||||
/* 北向串口不在这些场景的执行路径中,意外调用立即失败。 */
|
||||
rt_uint8_t u16_to_u8v(rt_uint16_t value, rt_uint8_t *buf)
|
||||
{ (void)value; (void)buf; CHECK(0); return 0; }
|
||||
int chrg_north_pkg_encode(eIDX_ID id, rt_uint8_t cmd, rt_uint8_t *in,
|
||||
rt_uint8_t len, rt_uint8_t *out, rt_uint16_t *out_len)
|
||||
{ (void)id; (void)cmd; (void)in; (void)len; (void)out; (void)out_len; CHECK(0); return -1; }
|
||||
int chrg_north_send(rt_uint8_t *data, rt_size_t length)
|
||||
{ (void)data; (void)length; CHECK(0); return -1; }
|
||||
|
||||
static struct chrg_switch_t *fixture(void)
|
||||
{
|
||||
struct chrg_switch_t *p = &chrgnorth.sw[0];
|
||||
p->id = ID_SINK;
|
||||
p->CV_mode = MODE_CONSTANT_CURRENT;
|
||||
p->sink.On_work = 1;
|
||||
p->sink.Now_State = 1;
|
||||
p->sink.loadc = 1000;
|
||||
p->sink.volt_on = 4000;
|
||||
p->sink.pro_loadv = 5000;
|
||||
p->sink.pro_loadc = 1000;
|
||||
p->sink.work_mode = SINK_WORK_ON_TEST_SHORT;
|
||||
p->sink.test.test_mode = SINK_TEST_SHORT;
|
||||
p->sink.test.test_mode_old = SINK_TEST_SHORT;
|
||||
return p;
|
||||
}
|
||||
static void exit_short(struct chrg_switch_t *p)
|
||||
{
|
||||
struct chrg_switch_t shadow = *p;
|
||||
shadow.sink.test.test_mode = 0;
|
||||
CHECK(chrg_comm_apply_sink_effect(0, p, &shadow) == MB_EX_NONE);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_INIT_AUX_WAIT);
|
||||
CHECK(pending && last_reg == 0x40 && last_val == 0);
|
||||
}
|
||||
static void ack(void) { pending = 0; result = 1; }
|
||||
static void complete_init(struct chrg_switch_t *p)
|
||||
{
|
||||
ack(); CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(pending && last_reg == REG_REL_ON && last_val == ALL_REL_OFF);
|
||||
ack(); CHECK(chrg_sink_work(0, p) == 0);
|
||||
now += 31; CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(pending && last_reg == REG_REL_ON && last_val == FU_REL_ON);
|
||||
ack(); CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_WAIT);
|
||||
CHECK(!g_sink_init_in_progress[0] && p->sink.test.test_mode_old == 0);
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct chrg_switch_t *p = fixture();
|
||||
struct chrg_switch_t s;
|
||||
int cancels, submits;
|
||||
CHECK(argc == 2);
|
||||
if (!strcmp(argv[1], "preserve_runtime")) {
|
||||
p->sink.test.test_mode = p->sink.test.test_mode_old = 0;
|
||||
p->sink.work_mode = SINK_WORK_RUNING;
|
||||
p->sub = IDX_GET_SINK_VC;
|
||||
p->Now_voltage = 5010; p->Now_current = 1000;
|
||||
p->Sink_voltage_seq = 77;
|
||||
CHECK(chrg_sink_work(0, p) == 0); ack();
|
||||
s = *p;
|
||||
s.sink.work_mode = SINK_WORK_INIT; s.sink.On_work = 0;
|
||||
s.sub = IDX_SET_SINK_CC_BEFORE_PD;
|
||||
s.Now_voltage = 0; s.Now_current = 0; s.Sink_voltage_seq = 1;
|
||||
CHECK(chrg_comm_apply_sink_effect(0, p, &s) == MB_EX_NONE);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_RUNING && p->sink.On_work == 1);
|
||||
CHECK(p->sub == IDX_GET_SINK_VC && p->Now_voltage == 5010);
|
||||
CHECK(p->Now_current == 1000 && p->Sink_voltage_seq == 77 && !pending);
|
||||
} else if (!strcmp(argv[1], "first_submit_failure")) {
|
||||
s = *p; s.sink.test.test_mode = 0; fail_submit = 1;
|
||||
CHECK(chrg_comm_apply_sink_effect(0, p, &s) == MB_EX_SLAVE_BUSY);
|
||||
CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(pending && last_reg == 0x40);
|
||||
complete_init(p);
|
||||
} else {
|
||||
exit_short(p);
|
||||
cancels = cancel_count; submits = submit_count; s = *p;
|
||||
if (!strcmp(argv[1], "duplicate_version") || !strcmp(argv[1], "busy_version")) {
|
||||
int busy = !strcmp(argv[1], "busy_version");
|
||||
chrgnorth.config_version[0] = 17;
|
||||
if (busy) s.sink.loadc = 800;
|
||||
CHECK(chrg_mb_write_sys(0, SYS_REG_PARAM_EFFECT, 1, &s,
|
||||
&chrgnorth, &chrgsouth) == (busy ? MB_EX_SLAVE_BUSY : MB_EX_NONE));
|
||||
CHECK(chrgnorth.config_version[0] == 17);
|
||||
CHECK(g_shadow_dirty[0] == busy);
|
||||
CHECK(cancel_count == cancels && pending);
|
||||
} else if (!strcmp(argv[1], "duplicate_exit")) {
|
||||
CHECK(chrg_comm_apply_sink_effect(0, p, &s) == MB_EX_NONE);
|
||||
CHECK(cancel_count == cancels && submit_count == submits);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_INIT_AUX_WAIT && pending);
|
||||
complete_init(p);
|
||||
} else if (!strcmp(argv[1], "busy_changed_config")) {
|
||||
struct chrg_switch_t before = *p;
|
||||
s.sink.loadc = 800; s.sink.volt_on = 4500;
|
||||
CHECK(chrg_comm_apply_sink_effect(0, p, &s) == MB_EX_SLAVE_BUSY);
|
||||
CHECK(memcmp(p, &before, sizeof(*p)) == 0);
|
||||
CHECK(cancel_count == cancels && submit_count == submits && pending);
|
||||
} else if (!strcmp(argv[1], "recover_init")) {
|
||||
p->sink.work_mode = SINK_WORK_INIT; /* 硬件截图的状态组合。 */
|
||||
CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_INIT_AUX_WAIT);
|
||||
CHECK(cancel_count == cancels && submit_count == submits);
|
||||
complete_init(p);
|
||||
} else if (!strcmp(argv[1], "stop_during_init")) {
|
||||
s.sink.Now_State = 2;
|
||||
CHECK(chrg_comm_apply_sink_effect(0, p, &s) == MB_EX_NONE);
|
||||
CHECK(!g_sink_init_in_progress[0] && p->sink.On_work == 0);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_STOP_WAIT);
|
||||
CHECK(pending && last_reg == REG_WORK && last_val == WORK_STOP);
|
||||
} else if (!strcmp(argv[1], "stop_submit_retry")) {
|
||||
s.sink.Now_State = 2; fail_submit = 1;
|
||||
CHECK(chrg_comm_apply_sink_effect(0, p, &s) == MB_EX_SLAVE_BUSY);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_STOP && p->sink.On_work == 0);
|
||||
CHECK(poll_state(p) == 0);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_STOP_WAIT);
|
||||
CHECK(pending && last_reg == REG_WORK && last_val == WORK_STOP);
|
||||
} else if (!strcmp(argv[1], "role_roundtrip")) {
|
||||
ack(); CHECK(chrg_sink_work(0, p) == 0);
|
||||
ack(); CHECK(chrg_sink_work(0, p) == 0);
|
||||
now += 31; CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(g_sink_init_stage[0] == SINK_INIT_STAGE_REST);
|
||||
/* 模拟Source接管时取消旧批次并清On_work,保留真实Sink静态上下文。 */
|
||||
CHECK(chrg_sou_com_batch_cancel(0) == 0);
|
||||
p->sink.On_work = 0;
|
||||
p->source.On_Flag = 1;
|
||||
p->sink.work_mode = SINK_WORK_SWITCH;
|
||||
queue_capacity = 2;
|
||||
CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_INIT);
|
||||
ack(); /* 角色切换批次已成功,但不代表新Sink初始化成功。 */
|
||||
CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_INIT_AUX_WAIT);
|
||||
CHECK(p->sink.On_work == 1 && pending && last_reg == 0x40);
|
||||
complete_init(p);
|
||||
} else if (!strcmp(argv[1], "rest_submit_failure")) {
|
||||
ack(); CHECK(chrg_sink_work(0, p) == 0);
|
||||
ack(); CHECK(chrg_sink_work(0, p) == 0);
|
||||
now += 31; fail_submit = 1;
|
||||
CHECK(chrg_sink_work(0, p) != 0);
|
||||
CHECK(!pending && result == 1);
|
||||
CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(pending && last_reg == REG_REL_ON && last_val == FU_REL_ON);
|
||||
CHECK(g_sink_init_in_progress[0] && p->sink.test.test_mode_old == 1);
|
||||
ack(); CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_WAIT);
|
||||
} else if (!strcmp(argv[1], "stage_submit_failure")) {
|
||||
ack(); fail_submit = 1;
|
||||
CHECK(chrg_sink_work(0, p) != 0);
|
||||
CHECK(!pending && result == 1); /* 故意保留上一阶段成功结果。 */
|
||||
CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(pending && last_reg == REG_REL_ON && last_val == ALL_REL_OFF);
|
||||
CHECK(g_sink_init_stage[0] == SINK_INIT_STAGE_AUX_RELAY);
|
||||
} else if (!strcmp(argv[1], "failed_batch_retry")) {
|
||||
pending = 0; result = -1;
|
||||
CHECK(chrg_sink_work(0, p) == 0);
|
||||
CHECK(pending && last_reg == 0x40 && p->sink.test.test_mode_old == 1);
|
||||
complete_init(p);
|
||||
} else if (!strcmp(argv[1], "complete_then_restart")) {
|
||||
complete_init(p); s = *p; s.sink.test.test_mode = 1;
|
||||
CHECK(chrg_comm_apply_sink_effect(0, p, &s) == MB_EX_NONE);
|
||||
CHECK(p->sink.work_mode == SINK_WORK_ON_TEST_SHORT && pending);
|
||||
ack(); exit_short(p); complete_init(p);
|
||||
} else { CHECK(0); }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include "rtthread.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "rtthread.h"
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef HOST_RTTHREAD_H
|
||||
#define HOST_RTTHREAD_H
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
typedef uint8_t rt_uint8_t;
|
||||
typedef int8_t rt_int8_t;
|
||||
typedef uint16_t rt_uint16_t;
|
||||
typedef int16_t rt_int16_t;
|
||||
typedef uint32_t rt_uint32_t;
|
||||
typedef int32_t rt_int32_t;
|
||||
typedef uintptr_t rt_ubase_t;
|
||||
typedef intptr_t rt_base_t;
|
||||
typedef size_t rt_size_t;
|
||||
typedef int rt_bool_t;
|
||||
typedef int rt_err_t;
|
||||
typedef uint32_t rt_tick_t;
|
||||
typedef void *rt_device_t;
|
||||
typedef void *rt_mutex_t;
|
||||
typedef void *rt_event_t;
|
||||
typedef void *rt_sem_t;
|
||||
typedef void *rt_mailbox_t;
|
||||
typedef void *rt_mq_t;
|
||||
#define RT_NULL NULL
|
||||
#define RT_TRUE 1
|
||||
#define RT_FALSE 0
|
||||
#define RT_WAITING_FOREVER (-1)
|
||||
#define RT_TICK_PER_SECOND 1000
|
||||
#define rt_memcpy memcpy
|
||||
#define rt_memcmp memcmp
|
||||
#define rt_memset memset
|
||||
rt_tick_t rt_tick_get(void);
|
||||
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms);
|
||||
int rt_kprintf(const char *format, ...);
|
||||
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout);
|
||||
rt_err_t rt_mutex_release(rt_mutex_t mutex);
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
// 使用真实 Sink 实现及 COM 生效函数;仅替换时钟、继电器和南向传输。
|
||||
// node tests/sink_init/run.cjs [gcc路径]。临时编译产物不写入固件工程。
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const cp = require('node:child_process');
|
||||
const root = path.resolve(__dirname, '../..');
|
||||
const app = path.join(root, 'apps/chrg/applications');
|
||||
const cc = process.argv[2] || process.env.CC || 'gcc';
|
||||
if (path.isAbsolute(cc)) process.env.PATH = path.dirname(cc) + path.delimiter + process.env.PATH;
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'chrg-sink-test-'));
|
||||
const source = fs.readFileSync(path.join(app, 'thread/chrg_comm.c'), 'utf8');
|
||||
function definition(name) {
|
||||
const pattern = new RegExp('static\\s+[\\w]+\\s+' + name + '\\s*\\([^;{]*\\)\\s*\\{', 'g');
|
||||
const match = pattern.exec(source);
|
||||
if (!match) return '';
|
||||
let depth = 1, end = pattern.lastIndex;
|
||||
while (depth && end < source.length) {
|
||||
if (source[end] === '{') depth++;
|
||||
if (source[end] === '}') depth--;
|
||||
end++;
|
||||
}
|
||||
return source.slice(match.index, end);
|
||||
}
|
||||
const type = source.match(/typedef struct\s*\{[^}]*\}\s*chrg_param_diff_t;/)[0];
|
||||
const functions = ['chrg_comm_get_param_diff', 'chrg_comm_sink_config_equal',
|
||||
'chrg_comm_copy_sink_config', 'chrg_comm_handle_sink_load_current',
|
||||
'chrg_sink_diff_send_reg', 'chrg_comm_apply_sink_effect', 'chrg_mb_write_sys'];
|
||||
const roll = fs.readFileSync(path.join(app, 'thread/chrg_roll_nor.c'), 'utf8');
|
||||
const dispatch = roll.slice(roll.indexOf('void chrg_roll_nor_thread_entry'))
|
||||
.match(/if \(\(pSW->id == ID_SOURCE\)[\s\S]+?state_work_ret = chrg_sink_work[\s\S]+?\n\t\t\t\}/)[0];
|
||||
const unit = '#include "' + path.join(app, 'utils/chrg_sink.c').replaceAll('\\', '/') + '"\n' +
|
||||
'#include "chrg_thread.h"\n#define LOG_E(...) ((void)0)\n' +
|
||||
'static rt_uint8_t g_shadow_dirty[4], last_pd_pro[4];\n#define SOURCE_PD_STATE_UNKNOWN 255\n' +
|
||||
'static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch, struct chrg_switch_t *p, const struct chrg_switch_t *s);\n' +
|
||||
type + '\n' + functions.map(definition).join('\n') + '\n' +
|
||||
'static int poll_state(struct chrg_switch_t *pSW) { int idx=0, state_work_ret=0;\n' + dispatch + '\nreturn state_work_ret; }\n' +
|
||||
fs.readFileSync(path.join(__dirname, 'cases.c'), 'utf8');
|
||||
const scenarios = ['duplicate_exit', 'preserve_runtime', 'busy_changed_config',
|
||||
'recover_init', 'stop_during_init', 'first_submit_failure', 'stage_submit_failure',
|
||||
'failed_batch_retry', 'complete_then_restart', 'duplicate_version',
|
||||
'busy_version', 'stop_submit_retry', 'rest_submit_failure', 'role_roundtrip'];
|
||||
try {
|
||||
fs.writeFileSync(path.join(dir, 'test.c'), unit);
|
||||
const exe = path.join(dir, process.platform === 'win32' ? 'test.exe' : 'test');
|
||||
const args = ['-std=c99', '-O0', '-ffunction-sections', '-fdata-sections',
|
||||
'-Werror=implicit-function-declaration', '-Wl,--gc-sections',
|
||||
'-I' + __dirname, ...['utils', 'thread', 'bsp'].map(p => '-I' + path.join(app, p)),
|
||||
path.join(dir, 'test.c'), '-o', exe];
|
||||
const build = cp.spawnSync(cc, args, {encoding: 'utf8'});
|
||||
if (build.status !== 0) throw Error(build.stderr || String(build.error || build.status));
|
||||
let failed = 0;
|
||||
for (const name of scenarios) {
|
||||
const run = cp.spawnSync(exe, [name], {encoding: 'utf8'});
|
||||
console.log((run.status === 0 ? 'PASS ' : 'FAIL ') + name);
|
||||
if (run.status !== 0) { failed++; console.log(run.stdout + run.stderr); }
|
||||
}
|
||||
console.log(`${scenarios.length - failed}/${scenarios.length} passed`);
|
||||
process.exitCode = failed ? 1 : 0;
|
||||
} finally {
|
||||
fs.rmSync(dir, {recursive: true, force: true});
|
||||
}
|
||||
Reference in New Issue
Block a user