Skip to content

Commit 7e77a6e

Browse files
authored
C Source View (#62)
This adds a collapsible C source line view on the right, which contains all C files from the log and their C lines. This view is in sync with the main log lines view; meaning if a user clicks on a line in one, we'll hightlight the associated lines in the other. This also removes C lines from the main log view by default but a checkbox was added so users can add them back in as desired. Additional changes: - remove line numbers from the main log view - remove Go To Line input box - Add C Line to State Panel
1 parent f3c0979 commit 7e77a6e

File tree

11 files changed

+1413
-272
lines changed

11 files changed

+1413
-272
lines changed

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ export default [
4747
},
4848
},
4949
{
50-
ignores: ['node_modules/**', 'dist/**', 'coverage/**'],
50+
ignores: ['node_modules/**', 'dist/**', 'coverage/**', 'src/__snapshots__/**'],
5151
},
5252
];

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"serve": "vite preview",
1313
"test": "jest",
1414
"format": "prettier src/* --write",
15-
"lint": "eslint src/*",
15+
"lint": "eslint",
1616
"typecheck": "tsc --noEmit"
1717
},
1818
"eslintConfig": {

src/App.test.tsx

Lines changed: 136 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,50 +4,22 @@
44
import "@testing-library/jest-dom";
55
import { render, createEvent, fireEvent } from "@testing-library/react";
66
import App from "./App";
7+
import { SAMPLE_LOG_DATA_1, SAMPLE_LOG_DATA_2 } from "./test-data";
78

8-
describe("App", () => {
9-
it("does not throw", () => {
10-
render(<App />);
11-
12-
const inputEl = document.getElementById("input-text");
13-
expect(inputEl).toBeTruthy();
14-
expect(inputEl).toBeVisible();
15-
});
9+
const DOM_EL_FAIL = "DOM Element missing";
1610

11+
describe("App", () => {
1712
it("renders the correct starting elements", () => {
18-
render(<App />);
19-
20-
const exampleLinkEl = document.getElementById("example-link");
21-
expect(exampleLinkEl?.innerHTML).toBe("Load an example log");
22-
23-
const inputEl = document.getElementById("input-text");
24-
expect(inputEl?.getAttribute("placeholder")).toBe(
25-
"Paste a verifier log here or choose a file",
26-
);
27-
expect(inputEl?.tagName).toBe("TEXTAREA");
28-
29-
const fileInputEl = document.getElementById("file-input");
30-
expect(fileInputEl?.tagName).toBe("INPUT");
31-
32-
const gotoLineEl = document.getElementById("goto-line-input");
33-
expect(gotoLineEl?.tagName).toBe("INPUT");
34-
35-
const gotoStartEl = document.getElementById("goto-start");
36-
expect(gotoStartEl?.tagName).toBe("BUTTON");
37-
38-
const gotoEndEl = document.getElementById("goto-end");
39-
expect(gotoEndEl?.tagName).toBe("BUTTON");
40-
41-
const clearEl = document.getElementById("clear");
42-
expect(clearEl?.tagName).toBe("BUTTON");
13+
const { container } = render(<App />);
14+
expect(container).toMatchSnapshot();
4315
});
4416

4517
it("renders the log visualizer when text is pasted", async () => {
46-
render(<App />);
18+
const { container, rerender } = render(<App />);
4719

4820
const inputEl = document.getElementById("input-text");
4921
if (!inputEl) {
50-
fail();
22+
throw new Error(DOM_EL_FAIL);
5123
}
5224

5325
fireEvent(
@@ -60,90 +32,60 @@ describe("App", () => {
6032
}),
6133
);
6234

63-
const logContainerEl = document.getElementById("log-container");
64-
expect(logContainerEl).toBeTruthy();
65-
expect(logContainerEl).toBeVisible();
66-
67-
const logLinesEl = document.getElementById("line-numbers-idx");
68-
expect(logLinesEl?.innerHTML).toBe(
69-
'<div class="line-numbers-line">1</div>',
70-
);
71-
72-
const logLinesPcEl = document.getElementById("line-numbers-pc");
73-
expect(logLinesPcEl?.innerHTML).toBe(
74-
'<div class="line-numbers-line">314:</div>',
75-
);
35+
rerender(<App />);
36+
expect(container).toMatchSnapshot();
7637

38+
expect(document.getElementById("c-source-container")).toBeTruthy();
7739
expect(document.getElementById("formatted-log-lines")).toBeTruthy();
78-
79-
const firstLine = document.getElementById("line-0");
80-
expect(firstLine?.innerHTML).toBe(
81-
'*(u8 *)(<span id="mem-slot-r7-line-0" class="mem-slot r7" data-id="r7">r7</span> +1303)&nbsp;=&nbsp;<span id="mem-slot-r1-line-0" class="mem-slot r1" data-id="r1">r1</span>',
82-
);
83-
84-
const hintSelectedLineEl = document.getElementById("hint-selected-line");
85-
expect(hintSelectedLineEl?.innerHTML).toBe(
86-
"<span>[selected raw line] 1:</span>&nbsp;314: (73) *(u8 *)(r7 +1303) = r1 ; frame1: R1_w=0 R7=map_value(off=0,ks=4,vs=2808,imm=0)",
87-
);
88-
89-
expect(document.getElementById("state-panel-shown")).toBeTruthy();
90-
91-
const statePanelHeader = document.getElementById("state-panel-header");
92-
expect(statePanelHeader?.innerHTML).toBe(
93-
"<div>Line: 1</div><div>PC: 314</div><div>Frame: 0</div>",
94-
);
95-
96-
//TODO: add tests for state panel content
40+
expect(document.getElementById("state-panel")).toBeTruthy();
9741

9842
// Hit the clear button and make sure we go back to the intial state
9943
const clearEl = document.getElementById("clear");
10044
if (!clearEl) {
101-
fail();
45+
throw new Error(DOM_EL_FAIL);
10246
}
10347
fireEvent(clearEl, createEvent.click(clearEl));
104-
expect(document.getElementById("log-container")).toBeFalsy();
105-
expect(document.getElementById("state-panel-shown")).toBeFalsy();
106-
expect(document.getElementById("input-text")).toBeTruthy();
107-
expect(document.getElementById("input-text")).toBeVisible();
48+
49+
rerender(<App />);
50+
expect(container).toMatchSnapshot();
51+
52+
expect(document.getElementById("c-source-container")).toBeFalsy();
53+
expect(document.getElementById("formatted-log-lines")).toBeFalsy();
54+
expect(document.getElementById("state-panel")).toBeFalsy();
10855
});
10956

11057
it("jumps to the next/prev instruction on key up/down", async () => {
11158
render(<App />);
11259

11360
const inputEl = document.getElementById("input-text");
11461
if (!inputEl) {
115-
fail();
62+
throw new Error(DOM_EL_FAIL);
11663
}
11764

11865
fireEvent(
11966
inputEl,
12067
createEvent.paste(inputEl, {
12168
clipboardData: {
122-
getData: () =>
123-
`
124-
0: (18) r1 = 0x11 ; R1_w=17
125-
2: (b7) r2 = 0 ; R2_w=0
126-
3: (85) call bpf_obj_new_impl#54651 ; R0_w=ptr_or_null_node_data(id=2,ref_obj_id=2) refs=2
127-
4: (bf) r6 = r0 ; R0_w=ptr_or_null_node_data(id=2,ref_obj_id=2) R6_w=ptr_or_null_node_data(id=2,ref_obj_id=2) refs=2
128-
5: (b7) r7 = 1 ; R7_w=1 refs=2
129-
; if (!n) @ rbtree.c:199
130-
6: (15) if r6 == 0x0 goto pc+104 ; R6_w=ptr_node_data(ref_obj_id=2) refs=2
131-
7: (b7) r1 = 4 ; R1_w=4 ref
132-
`,
69+
getData: () => SAMPLE_LOG_DATA_1,
13370
},
13471
}),
13572
);
13673

74+
// Need to show all log lines
75+
const checkboxEl = document.getElementById("show-full-log");
76+
if (!checkboxEl) {
77+
throw new Error(DOM_EL_FAIL);
78+
}
79+
fireEvent(checkboxEl, createEvent.click(checkboxEl));
80+
13781
const logContainerEl = document.getElementById("log-container");
138-
expect(logContainerEl).toBeTruthy();
139-
expect(logContainerEl).toBeVisible();
14082

14183
const line5 = document.getElementById("line-5");
14284
const line6 = document.getElementById("line-6");
14385
const line7 = document.getElementById("line-7");
14486

145-
if (!line5 || !line6 || !line7 || !logContainerEl) {
146-
fail();
87+
if (!line5 || !line7 || !line6 || !logContainerEl) {
88+
throw new Error(DOM_EL_FAIL);
14789
}
14890

14991
expect(line5.innerHTML).toBe(
@@ -174,4 +116,110 @@ describe("App", () => {
174116
expect(line6.classList.contains("selected-line")).toBeFalsy();
175117
expect(line7.classList.contains("selected-line")).toBeFalsy();
176118
});
119+
120+
it("c lines and state panel containers are collapsible", async () => {
121+
render(<App />);
122+
123+
const inputEl = document.getElementById("input-text");
124+
if (!inputEl) {
125+
throw new Error("Input text is missing");
126+
}
127+
128+
fireEvent(
129+
inputEl,
130+
createEvent.paste(inputEl, {
131+
clipboardData: {
132+
getData: () => SAMPLE_LOG_DATA_1,
133+
},
134+
}),
135+
);
136+
137+
const cSourceEl = document.getElementById("c-source-container");
138+
const cSourceFile = cSourceEl?.querySelector(".c-source-file");
139+
140+
expect(cSourceFile).toBeVisible();
141+
142+
const cSourceHideShow = cSourceEl?.querySelector(".hide-show-button");
143+
144+
if (!cSourceHideShow) {
145+
throw new Error(DOM_EL_FAIL);
146+
}
147+
148+
fireEvent(cSourceHideShow, createEvent.click(cSourceHideShow));
149+
expect(cSourceFile).not.toBeVisible();
150+
151+
const statePanelEl = document.getElementById("state-panel");
152+
const statePanelHeader = document.getElementById("state-panel-header");
153+
154+
expect(statePanelHeader).toBeVisible();
155+
156+
const statePanelHideShow = statePanelEl?.querySelector(".hide-show-button");
157+
158+
if (!statePanelHideShow) {
159+
throw new Error(DOM_EL_FAIL);
160+
}
161+
162+
fireEvent(statePanelHideShow, createEvent.click(statePanelHideShow));
163+
expect(statePanelHeader).not.toBeVisible();
164+
});
165+
166+
it("highlights the associated c source or log line(s) when the other is clicked ", async () => {
167+
render(<App />);
168+
169+
const inputEl = document.getElementById("input-text");
170+
if (!inputEl) {
171+
throw new Error(DOM_EL_FAIL);
172+
}
173+
174+
fireEvent(
175+
inputEl,
176+
createEvent.paste(inputEl, {
177+
clipboardData: {
178+
getData: () => SAMPLE_LOG_DATA_2,
179+
},
180+
}),
181+
);
182+
183+
const line4El = document.getElementById("line-4");
184+
const cLineEl = document.getElementById("line-rbtree.c:198");
185+
if (!line4El || !cLineEl) {
186+
throw new Error(DOM_EL_FAIL);
187+
}
188+
189+
expect(line4El.classList).not.toContain("selected-line");
190+
expect(cLineEl.classList).not.toContain("selected-line");
191+
192+
// Click on the first instruction log line
193+
fireEvent(line4El, createEvent.click(line4El));
194+
195+
expect(line4El.classList).toContain("selected-line");
196+
expect(cLineEl.classList).toContain("selected-line");
197+
198+
// Click on another log line
199+
const line10El = document.getElementById("line-10");
200+
if (!line10El) {
201+
throw new Error(DOM_EL_FAIL);
202+
}
203+
204+
fireEvent(line10El, createEvent.click(line10El));
205+
206+
expect(line4El.classList).not.toContain("selected-line");
207+
expect(cLineEl.classList).not.toContain("selected-line");
208+
209+
// Click on the first c source line
210+
fireEvent(cLineEl, createEvent.click(cLineEl));
211+
212+
expect(line4El.classList).toContain("selected-line");
213+
expect(cLineEl.classList).toContain("selected-line");
214+
215+
// The other instructions for this source line should also be selected
216+
const followingIns = ["line-5", "line-6", "line-7", "line-8"];
217+
followingIns.forEach((lineId) => {
218+
const el = document.getElementById(lineId);
219+
if (!el) {
220+
throw new Error(DOM_EL_FAIL);
221+
}
222+
expect(el.classList).toContain("selected-line");
223+
});
224+
});
177225
});

0 commit comments

Comments
 (0)