Skip to content

Commit 85043e9

Browse files
committed
Update Stress Test
1 parent a973618 commit 85043e9

1 file changed

Lines changed: 136 additions & 48 deletions

File tree

examples/C/stress_test/main.c

Lines changed: 136 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ static size_t status_win = 0;
3939

4040
static volatile long g_start = 0;
4141
static volatile long g_abort = 0;
42+
static volatile long g_run_one = -1; // Index of a single stage to run
43+
static volatile long g_busy = 0; // A stage is running right now
4244
static volatile long g_finished = 0; // 1 = all passed, 2 = a stage failed
4345
static volatile long g_ready = 0;
4446
static volatile long g_report = -1;
@@ -161,9 +163,19 @@ static void mark_passed(size_t w, const char* label) {
161163

162164
static void cb_start(webui_event_t* e) {
163165
(void)e;
166+
if (g_busy)
167+
return;
164168
g_start = 1;
165169
}
166170

171+
static void cb_run_stage(webui_event_t* e) {
172+
if (g_busy)
173+
return;
174+
long idx = (long)webui_get_int(e);
175+
if (idx >= 0 && idx < STAGES)
176+
g_run_one = idx;
177+
}
178+
167179
static void cb_exit(webui_event_t* e) {
168180
(void)e;
169181
g_abort = 1;
@@ -513,6 +525,11 @@ static bool stage_port_reload(int i) {
513525
snprintf(detail, sizeof(detail), "First show failed");
514526
ok = false;
515527
}
528+
char old_url[256] = {0};
529+
if (ok) {
530+
const char* u = webui_get_url(w);
531+
snprintf(old_url, sizeof(old_url), "%s", (u != NULL ? u : ""));
532+
}
516533
size_t new_port = 0;
517534
if (ok) {
518535
for (size_t p = 33000; p < 33100; p++) {
@@ -531,21 +548,25 @@ static bool stage_port_reload(int i) {
531548
ok = false;
532549
}
533550
if (ok) {
534-
sleep_ms(1500);
535-
g_ready = 0;
536-
if (!webui_show(w, make_page("Port reload", "ready(function(){webui.call('stage_ready',1);});")) ||
537-
!wait_long(&g_ready, 1, 15000)) {
538-
snprintf(detail, sizeof(detail), "Re-show on the new port failed");
539-
ok = false;
540-
}
541-
}
542-
if (ok) {
551+
// This blocks until the pending reload is done,
552+
// so the returned URL is the one of the new server
543553
const char* url = webui_get_url(w);
544554
char expect[32];
545555
snprintf(expect, sizeof(expect), ":%zu", new_port);
546556
if (url == NULL || strstr(url, expect) == NULL) {
547557
snprintf(detail, sizeof(detail), "URL [%s] is not on port %zu", (url != NULL ? url : "?"), new_port);
548558
ok = false;
559+
} else if (strcmp(old_url, url) == 0) {
560+
snprintf(detail, sizeof(detail), "URL did not change after the reload");
561+
ok = false;
562+
}
563+
}
564+
if (ok) {
565+
g_ready = 0;
566+
if (!webui_show(w, make_page("Port reload", "ready(function(){webui.call('stage_ready',1);});")) ||
567+
!wait_long(&g_ready, 1, 15000)) {
568+
snprintf(detail, sizeof(detail), "Re-show on the new port failed");
569+
ok = false;
549570
}
550571
}
551572
if (ok)
@@ -702,12 +723,27 @@ static bool stage_destroy_from_callback(int i) {
702723
bool ok = true;
703724
char detail[256] = "webui_destroy() called inside its own callback";
704725
size_t w = webui_new_window();
726+
webui_bind(w, "stage_ready", cb_ready);
705727
webui_bind(w, "kamikaze", cb_kamikaze);
728+
g_ready = 0;
706729
g_kamikaze = 0;
707-
if (!webui_show(w, make_page("Destroy from callback", "ready(function(){webui.call('kamikaze');});"))) {
730+
// The page only reports that it is connected here. Destroying the
731+
// window from the connect callback itself would race `webui_show()`,
732+
// which returns the connection status: the window would already be
733+
// gone by the time it returns, and the show would look failed.
734+
if (!webui_show(w, make_page("Destroy from callback", "ready(function(){webui.call('stage_ready',1);});"))) {
708735
snprintf(detail, sizeof(detail), "Show failed");
709736
ok = false;
710737
}
738+
if (ok && !wait_long(&g_ready, 1, 15000)) {
739+
snprintf(detail, sizeof(detail), "No page signal");
740+
ok = false;
741+
}
742+
if (ok) {
743+
// Now let the page call the backend function
744+
// that destroys this very window
745+
webui_run(w, "webui.call('kamikaze');");
746+
}
711747
if (ok && !wait_long(&g_kamikaze, 1, 15000)) {
712748
snprintf(detail, sizeof(detail), "Callback never ran");
713749
ok = false;
@@ -784,6 +820,60 @@ static const stage_fn stage_fns[STAGES] = {
784820
stage_rapid_open_close
785821
};
786822

823+
// Run every stage in order, and stop at the first failure. Only a full
824+
// run writes the result files, they tell if the whole suite passed.
825+
static void run_all_stages(void) {
826+
827+
remove("test_pass.txt");
828+
remove("test_error.txt");
829+
status_run("resetStages();");
830+
status_run("setBanner('Running all stages...','');");
831+
832+
int i;
833+
bool all = true;
834+
for (i = 0; i < STAGES && all && !g_abort; i++)
835+
all = stage_fns[i](i);
836+
837+
if (g_abort)
838+
return;
839+
840+
if (all) {
841+
g_finished = 1;
842+
FILE* f = fopen("test_pass.txt", "w");
843+
if (f != NULL)
844+
fclose(f);
845+
printf("ALL %d STAGES PASSED\n", STAGES);
846+
status_run("setBanner('ALL %d STAGES PASSED - every green window is still connected','pass');", STAGES);
847+
} else {
848+
g_finished = 2;
849+
for (int k = i; k < STAGES; k++)
850+
status_run("setStage(%d,'SKIP','Stopped after a failure');", k);
851+
FILE* f = fopen("test_error.txt", "w");
852+
if (f != NULL)
853+
fclose(f);
854+
printf("STOPPED: stage %d failed\n", i);
855+
status_run("setBanner('STOPPED - stage %d failed','fail');", i);
856+
}
857+
fflush(stdout);
858+
}
859+
860+
// Run one stage alone, for a user who wants to test a single case.
861+
// This does not touch the result files.
862+
static void run_single_stage(int idx) {
863+
864+
status_run("setBanner('Running stage %d alone...','');", idx + 1);
865+
866+
bool ok = stage_fns[idx](idx);
867+
868+
if (g_abort)
869+
return;
870+
871+
printf("SINGLE STAGE %d -> %s\n", idx + 1, ok ? "PASS" : "FAIL");
872+
fflush(stdout);
873+
status_run("setBanner('Stage %d %s (single run, result files unchanged)','%s');",
874+
idx + 1, ok ? "passed" : "failed", ok ? "pass" : "fail");
875+
}
876+
787877
#ifdef _WIN32
788878
static DWORD WINAPI driver(LPVOID arg)
789879
#else
@@ -792,38 +882,27 @@ static void* driver(void* arg)
792882
{
793883
(void)arg;
794884

795-
// Wait for the Start click, or start right away in auto mode
796-
while (!g_start && !g_abort)
797-
sleep_ms(50);
798-
if (!g_abort) {
799-
800-
status_run("document.getElementById('bstart').disabled=true;");
801-
status_run("setBanner('Running...','');");
802-
803-
int i;
804-
bool all = true;
805-
for (i = 0; i < STAGES && all && !g_abort; i++)
806-
all = stage_fns[i](i);
807-
808-
if (!g_abort) {
809-
if (all) {
810-
g_finished = 1;
811-
FILE* f = fopen("test_pass.txt", "w");
812-
if (f != NULL)
813-
fclose(f);
814-
printf("ALL %d STAGES PASSED\n", STAGES);
815-
status_run("setBanner('ALL %d STAGES PASSED - every green window is still connected','pass');", STAGES);
816-
} else {
817-
g_finished = 2;
818-
for (int k = i; k < STAGES; k++)
819-
status_run("setStage(%d,'SKIP','Stopped after a failure');", k);
820-
FILE* f = fopen("test_error.txt", "w");
821-
if (f != NULL)
822-
fclose(f);
823-
printf("STOPPED: stage %d failed\n", i);
824-
status_run("setBanner('STOPPED - stage %d failed','fail');", i);
825-
}
826-
fflush(stdout);
885+
// Serve run requests until the app exits. A request comes from
886+
// the Start button, from a stage Run button, or from auto mode.
887+
while (!g_abort) {
888+
889+
if (g_start) {
890+
g_start = 0;
891+
g_busy = 1;
892+
run_all_stages();
893+
g_busy = 0;
894+
status_run("setBusy(false);");
895+
}
896+
else if (g_run_one >= 0) {
897+
int idx = (int)g_run_one;
898+
g_run_one = -1;
899+
g_busy = 1;
900+
run_single_stage(idx);
901+
g_busy = 0;
902+
status_run("setBusy(false);");
903+
}
904+
else {
905+
sleep_ms(50);
827906
}
828907
}
829908

@@ -842,6 +921,8 @@ static const char* status_page =
842921
"border-radius:6px;cursor:pointer;background:#3d63dd;color:#fff}"
843922
"button:disabled{background:#2b3050;color:#8a92b2;cursor:default}"
844923
"#bexit{background:#5a2530}"
924+
"button.run{font-size:12px;font-weight:600;padding:4px 14px;margin:0;background:#2f3c6b}"
925+
"button.run:hover:enabled{background:#3d63dd}"
845926
"#banner{padding:10px 14px;border-radius:8px;background:#2b3050;margin:14px 0;font-weight:600}"
846927
"#banner.pass{background:#12391f;color:#7ce38b}"
847928
"#banner.fail{background:#46151a;color:#ff8089}"
@@ -853,11 +934,11 @@ static const char* status_page =
853934
"</style></head><body>"
854935
"<h1>WebUI Stress Test</h1>"
855936
"<div>"
856-
"<button id=\"bstart\" onclick=\"this.disabled=true;webui.call('start_test');\">Start</button>"
937+
"<button id=\"bstart\" onclick=\"setBusy(true);webui.call('start_test');\">Start</button>"
857938
"<button id=\"bexit\" onclick=\"webui.call('app_exit');\">Exit</button>"
858939
"</div>"
859-
"<div id=\"banner\">Ready. Click Start to run the test</div>"
860-
"<table id=\"tbl\"><tr><th>#</th><th>Stage</th><th class=\"st\">Status</th><th>Details</th></tr></table>"
940+
"<div id=\"banner\">Ready. Click Start to run every stage, or Run to test a single stage</div>"
941+
"<table id=\"tbl\"><tr><th>#</th><th>Stage</th><th class=\"st\">Status</th><th>Details</th><th></th></tr></table>"
861942
"<script>"
862943
"const names=['Dashboard connection','Open / close / reopen window',"
863944
"'C to JS calls (webui_run / webui_script)','JS to C calls and arguments',"
@@ -867,12 +948,18 @@ static const char* status_page =
867948
"const tbl=document.getElementById('tbl');"
868949
"names.forEach((n,i)=>{const r=tbl.insertRow(-1);r.insertCell(0).textContent=i+1;"
869950
"r.insertCell(1).textContent=n;const s=r.insertCell(2);s.className='st';s.id='st'+i;"
870-
"s.textContent='PENDING';const d=r.insertCell(3);d.className='dt';d.id='dt'+i;});"
951+
"s.textContent='PENDING';const d=r.insertCell(3);d.className='dt';d.id='dt'+i;"
952+
"const a=r.insertCell(4);const b=document.createElement('button');b.className='run';"
953+
"b.textContent='Run';b.onclick=function(){setBusy(true);webui.call('run_stage',i);};"
954+
"a.appendChild(b);});"
871955
"function setStage(i,st,dt){const s=document.getElementById('st'+i);s.textContent=st;"
872-
"s.className='st '+(st=='PASS'?'pass':st=='FAIL'?'fail':st=='SKIP'?'skip':'run');"
873-
"if(dt)document.getElementById('dt'+i).textContent=dt;}"
956+
"s.className='st '+(st=='PASS'?'pass':st=='FAIL'?'fail':st=='SKIP'?'skip':st=='PENDING'?'':'run');"
957+
"if(dt!==undefined)document.getElementById('dt'+i).textContent=dt;}"
874958
"function setBanner(t,cls){const b=document.getElementById('banner');"
875959
"b.textContent=t;b.className=cls||'';}"
960+
"function setBusy(b){document.getElementById('bstart').disabled=b;"
961+
"document.querySelectorAll('button.run').forEach(x=>{x.disabled=b;});}"
962+
"function resetStages(){for(let i=0;i<names.length;i++)setStage(i,'PENDING','');}"
876963
"</script></body></html>";
877964

878965
int main(int argc, char* argv[]) {
@@ -887,6 +974,7 @@ int main(int argc, char* argv[]) {
887974

888975
status_win = webui_new_window();
889976
webui_bind(status_win, "start_test", cb_start);
977+
webui_bind(status_win, "run_stage", cb_run_stage);
890978
webui_bind(status_win, "app_exit", cb_exit);
891979
if (!webui_show(status_win, status_page)) {
892980
printf("Could not open the status window\n");

0 commit comments

Comments
 (0)