diff --git a/os-checks/pages/testcases.vue b/os-checks/pages/testcases.vue index a5c4fb7..c631c33 100644 --- a/os-checks/pages/testcases.vue +++ b/os-checks/pages/testcases.vue @@ -98,7 +98,8 @@ import type { DataTableSortMeta } from 'primevue/datatable'; import { FilterMatchMode } from '@primevue/core/api'; import type { PkgInfo } from '~/shared/info'; -import { cloneDeep, uniq } from 'es-toolkit/compat'; +import type { TestResult, Selection, Options } from '~/shared/testcases'; +import { defaultOptions, applySelection, testcasesToOptions, summariesToTestResult } from '~/shared/testcases'; useHead({ title: 'Test Cases' }); @@ -126,74 +127,17 @@ watch(selectedTest, sel => dialogShow.value = sel ? true : false); githubFetch({ path: "plugin/cargo/info/summaries.json" }).then(val => { - testcases.value = summariesToTestResult(val); - testcasesFiltered.value = cloneDeep(testcases.value); + const tc = summariesToTestResult(val); + testcases.value = tc; + testcasesFiltered.value = applySelection(tc, selected); }); -type TestResult = { - idx: number, - user: string, - repo: string, - pkg: string, - bin: string, - kind: string, - name: string, - test_pass: string, - test_duration_ms: number | null, - test_error: string | null, - miri_pass: string, - miri_output: string | null, - miri_timeout: string, -}; - -function summariesToTestResult(pkg_info: PkgInfo[]): TestResult[] { - let result: TestResult[] = []; - let idx = 0; - - for (const info of pkg_info) { - for (const [pkg, value] of Object.entries(info.pkgs)) { - for (const test of value.testcases?.tests || []) { - for (const testcase of test.testcases) { - result.push({ - idx: idx++, - user: info.user, - repo: info.repo, - pkg, - bin: test.binary_name, - kind: test.kind, - name: testcase.name, - test_pass: testcase.status === "ok" ? "✅" : (testcase.status === "failed" ? "❌" : ""), - test_duration_ms: testcase.duration_ms, - test_error: testcase.error, - miri_pass: testcase.miri_pass ? "✅" : "❌", - miri_output: testcase.miri_output, - miri_timeout: testcase.miri_timeout ? "💥" : "", - }); - } - } - } - } - - return result; -} - function sortsChanged(meta?: DataTableSortMeta[] | null) { if (meta) { selected.sorts = meta; } } - -const selected = reactive<{ - user: string | null, - repo: string | null, - pkg: string | null, - kind: string | null, - test_pass: string | null, - miri_pass: string | null, - miri_timeout: string | null, - text: any, - sorts: DataTableSortMeta[], -}>({ +const selected = reactive({ user: null, repo: null, pkg: null, @@ -205,73 +149,71 @@ const selected = reactive<{ sorts: [], }); -type Options = { - user: string[], - repo: string[], - pkg: string[], - kind: string[], - test_pass: string[], - miri_pass: string[], - miri_timeout: string[], -}; -function defaultOptions(): Options { - return { - user: [], - repo: [], - pkg: [], - kind: [], - test_pass: [], - miri_pass: [], - miri_timeout: [], - }; -} const options = reactive<{ val: Options }>({ val: defaultOptions() }); // init options watch(testcases, tc => options.val = testcasesToOptions(tc)); // update table when filter selection changes -watch( - selected, - ({ user, repo, pkg, kind, test_pass, miri_pass, miri_timeout }) => { - // for simplicity, the data of testcases are supposed to remain unchanged - const chosen_testcases = testcases.value.filter(test => { - let chosen = true; - if (user) chosen &&= test.user === user; - if (repo) chosen &&= test.repo === repo; - if (pkg) chosen &&= test.pkg === pkg; - if (kind) chosen &&= test.kind === repo; - if (test_pass) chosen &&= test.test_pass === test_pass; - if (miri_pass) chosen &&= test.miri_pass === miri_pass; - if (miri_timeout || miri_timeout === "") chosen &&= test.miri_timeout === miri_timeout; - return chosen; - }); - testcasesFiltered.value = chosen_testcases.map((tc, idx) => { - tc.idx = idx; - return tc; +watch(selected, sel => testcasesFiltered.value = applySelection(testcases.value, sel)); + +// ******************* route query ******************* +const route = useRoute(); +function updateFilter(query: { + user?: string, + repo?: string, + pkg?: string, + kind?: string, + test_pass?: string, + miri_pass?: string, + miri_timeout?: string, + text?: string, + sorts?: string, +}) { + const { user, repo, pkg, kind, test_pass, miri_pass, miri_timeout, text, sorts } = query; + + // only support single value for each param + // FIXME: empty string will not handled + if (user) selected.user = decodeURIComponent(user); + if (repo) selected.repo = decodeURIComponent(repo); + if (pkg) selected.pkg = decodeURIComponent(pkg); + if (kind) selected.kind = decodeURIComponent(kind); + if (test_pass) selected.test_pass = decodeURIComponent(test_pass); + if (miri_pass) selected.miri_pass = decodeURIComponent(miri_pass); + if (miri_timeout) selected.miri_timeout = decodeURIComponent(miri_timeout); + if (text) selected.text.global.value = decodeURIComponent(text); + + if (sorts) { + const args = decodeURIComponent(sorts).split(","); + //@ts-ignore + selected.sorts = args.map(arg => { + let [field, order] = arg.split("="); + return { field, order: parseInt(order) }; }); - // options.val = testcasesToOptions(chosen_testcases); - }); - -function testcasesToOptions(tc: TestResult[]): Options { - let opts = defaultOptions(); - for (const test of tc) { - opts.user.push(test.user); - opts.repo.push(test.repo); - opts.pkg.push(test.pkg); - opts.kind.push(test.kind); - opts.test_pass.push(test.test_pass); - opts.miri_pass.push(test.miri_pass); - opts.miri_timeout.push(test.miri_timeout); } - - opts.user = uniq(opts.user).sort(); - opts.repo = uniq(opts.repo).sort(); - opts.pkg = uniq(opts.pkg).sort(); - opts.kind = uniq(opts.kind).sort(); - opts.test_pass = uniq(opts.test_pass).sort(); - opts.miri_pass = uniq(opts.miri_pass).sort(); - opts.miri_timeout = uniq(opts.miri_timeout).sort(); - return opts; } +updateFilter(route.query); + +const router = useRouter(); +watch(selected, sel => { + const { user, repo, pkg, kind, test_pass, miri_pass, miri_timeout, text, sorts } = sel; + + let query: any = {}; + + if (user) query.user = encodeURIComponent(user); + if (repo) query.repo = encodeURIComponent(repo); + if (pkg) query.pkg = encodeURIComponent(pkg); + if (kind) query.kind = encodeURIComponent(kind); + if (test_pass) query.test_pass = encodeURIComponent(test_pass); + if (miri_pass) query.miri_pass = encodeURIComponent(miri_pass); + if (miri_timeout) query.miri_timeout = encodeURIComponent(miri_timeout); + if (text.global.value) query.text = encodeURIComponent(text.global.value); + + if (sorts.length !== 0) { + const args = sorts.map(({ field, order }) => order ? `${field}=${order}` : null); + query.sorts = encodeURIComponent(args.filter(x => x).join(",")); + } + + router.push({ path: route.path, query }); +}); diff --git a/os-checks/shared/testcases.ts b/os-checks/shared/testcases.ts new file mode 100644 index 0000000..462c22c --- /dev/null +++ b/os-checks/shared/testcases.ts @@ -0,0 +1,129 @@ +import type { DataTableSortMeta } from 'primevue/datatable'; +import { uniq } from 'es-toolkit/compat'; +import type { PkgInfo } from './info'; + +export type TestResult = { + idx: number, + user: string, + repo: string, + pkg: string, + bin: string, + kind: string, + name: string, + test_pass: string, + test_duration_ms: number | null, + test_error: string | null, + miri_pass: string, + miri_output: string | null, + miri_timeout: string, +}; + +export type Selection = { + user: string | null, + repo: string | null, + pkg: string | null, + kind: string | null, + test_pass: string | null, + miri_pass: string | null, + miri_timeout: string | null, + text: any, + sorts: DataTableSortMeta[], +}; + +/** Called when testcases are fetched or selection is updated */ +export function applySelection( + testcases: TestResult[], + { user, repo, pkg, kind, test_pass, miri_pass, miri_timeout }: Selection +): TestResult[] { + // for simplicity, the data of testcases are supposed to remain unchanged + const chosen_testcases = testcases.filter(test => { + let chosen = true; + if (user) chosen &&= test.user === user; + if (repo) chosen &&= test.repo === repo; + if (pkg) chosen &&= test.pkg === pkg; + if (kind) chosen &&= test.kind === repo; + if (test_pass) chosen &&= test.test_pass === test_pass; + if (miri_pass) chosen &&= test.miri_pass === miri_pass; + if (miri_timeout || miri_timeout === "") chosen &&= test.miri_timeout === miri_timeout; + return chosen; + }); + return chosen_testcases.map((tc, idx) => { + tc.idx = idx; + return tc; + }); +} + +export type Options = { + user: string[], + repo: string[], + pkg: string[], + kind: string[], + test_pass: string[], + miri_pass: string[], + miri_timeout: string[], +}; + +export function defaultOptions(): Options { + return { + user: [], + repo: [], + pkg: [], + kind: [], + test_pass: [], + miri_pass: [], + miri_timeout: [], + }; +} + +export function testcasesToOptions(tc: TestResult[]): Options { + let opts = defaultOptions(); + for (const test of tc) { + opts.user.push(test.user); + opts.repo.push(test.repo); + opts.pkg.push(test.pkg); + opts.kind.push(test.kind); + opts.test_pass.push(test.test_pass); + opts.miri_pass.push(test.miri_pass); + opts.miri_timeout.push(test.miri_timeout); + } + + opts.user = uniq(opts.user).sort(); + opts.repo = uniq(opts.repo).sort(); + opts.pkg = uniq(opts.pkg).sort(); + opts.kind = uniq(opts.kind).sort(); + opts.test_pass = uniq(opts.test_pass).sort(); + opts.miri_pass = uniq(opts.miri_pass).sort(); + opts.miri_timeout = uniq(opts.miri_timeout).sort(); + return opts; +} + +export function summariesToTestResult(pkg_info: PkgInfo[]): TestResult[] { + let result: TestResult[] = []; + let idx = 0; + + for (const info of pkg_info) { + for (const [pkg, value] of Object.entries(info.pkgs)) { + for (const test of value.testcases?.tests || []) { + for (const testcase of test.testcases) { + result.push({ + idx: idx++, + user: info.user, + repo: info.repo, + pkg, + bin: test.binary_name, + kind: test.kind, + name: testcase.name, + test_pass: testcase.status === "ok" ? "✅" : (testcase.status === "failed" ? "❌" : ""), + test_duration_ms: testcase.duration_ms, + test_error: testcase.error, + miri_pass: testcase.miri_pass ? "✅" : "❌", + miri_output: testcase.miri_output, + miri_timeout: testcase.miri_timeout ? "💥" : "", + }); + } + } + } + } + + return result; +}