diff --git a/release_notes.md b/release_notes.md index 6fa9b60..7cad534 100644 --- a/release_notes.md +++ b/release_notes.md @@ -2,6 +2,8 @@ Under development in `master` branch. +- in report, added info on _executionTimeInSeconds_ and _evaluatedHttpCalls_ + # 0.1.0 - first public release diff --git a/src/main/resources/wfc/schemas/report.yaml b/src/main/resources/wfc/schemas/report.yaml index cd2ce3f..26e76bc 100644 --- a/src/main/resources/wfc/schemas/report.yaml +++ b/src/main/resources/wfc/schemas/report.yaml @@ -48,6 +48,10 @@ properties: type: array items: $ref: "#/$defs/TestCase" + executionTimeInSeconds: + type: integer + minimum: 0 + description: "For how long, in seconds, the tool was running in total." #OPTIONAL extra: description: "Extra, optional coverage information, collected by different tools." @@ -55,7 +59,17 @@ properties: items: $ref: "#/$defs/Coverage" -required: ["schemaVersion","toolName","toolVersion","creationTime","faults","problemDetails","totalTests","testFilePaths","testCases"] +required: + - "schemaVersion" + - "toolName" + - "toolVersion" + - "creationTime" + - "faults" + - "problemDetails" + - "totalTests" + - "testFilePaths" + - "testCases" + - "executionTimeInSeconds" $defs: OperationId: @@ -122,11 +136,18 @@ $defs: RESTReport: type: object properties: - totalHttpCalls: - description: "Total number of HTTP calls made in all the test cases. A test case could contain several HTTP calls, \ + outputHttpCalls: + description: "Total number of HTTP calls made in all the generated test cases given as output. \ + A test case could contain several HTTP calls, \ e.g., a POST followed by a GET and then a DELETE." type: integer minimum: 0 + evaluatedHttpCalls: + description: "Total number of all HTTP calls made during the whole fuzzing session. \ + If the fuzzing was left running for hours, millions of HTTP could have been made. + The output generated tests will only contain a tiny subset of these evaluated calls." + type: integer + minimum: 0 endpointIds: description: "Unique ids of all the endpoints in the tested API." type: array @@ -138,7 +159,7 @@ $defs: type: array items: $ref: "#/$defs/CoveredEndpoint" - required: ["totalHttpCalls","endpointIds","coveredHttpStatus"] + required: ["outputHttpCalls","evaluatedHttpCalls","endpointIds","coveredHttpStatus"] TestCase: type: object diff --git a/web-report/src-e2e/App.test.tsx b/web-report/src-e2e/App.test.tsx index 2fca749..e55b676 100644 --- a/web-report/src-e2e/App.test.tsx +++ b/web-report/src-e2e/App.test.tsx @@ -41,10 +41,10 @@ describe('App test', () => { it('handles successful data loading', async () => { render(); - + // Initially shows loading state expect(screen.getByText(/Please wait, files are loading.../)).toBeInTheDocument(); - + // Wait for loading to complete and verify header data await waitFor(() => { expect(screen.queryByText(/Please wait, files are loading.../)).toBeNull(); @@ -79,11 +79,13 @@ describe('App test', () => { render(); expect(screen.getByText(/Please wait, files are loading.../)).toBeInTheDocument(); const total = reportData.problemDetails.rest.endpointIds.length; - const totalHttpCalls = reportData.problemDetails.rest.totalHttpCalls; + const outputHttpCalls = reportData.problemDetails.rest.outputHttpCalls; + const evaluatedHttpCalls = reportData.problemDetails.rest.evaluatedHttpCalls; await waitFor(() => { expect(screen.getByTestId('rest-report-endpoint')).toContainHTML(`${total}`); - expect(screen.getByTestId('rest-report-http-calls')).toContainHTML(`${totalHttpCalls}`); + expect(screen.getByTestId('rest-report-output-http-calls')).toContainHTML(`${outputHttpCalls}`); + expect(screen.getByTestId('rest-report-evaluated-http-calls')).toContainHTML(`${evaluatedHttpCalls}`); }); }); @@ -143,4 +145,4 @@ describe('App test', () => { expect(screen.getByTestId('faults-component-fault-counts')).toContainHTML(faultCounts.length.toString()); }); }); -}); \ No newline at end of file +}); diff --git a/web-report/src-e2e/static/report.json b/web-report/src-e2e/static/report.json index 93a9940..7f32b47 100644 --- a/web-report/src-e2e/static/report.json +++ b/web-report/src-e2e/static/report.json @@ -3,6 +3,7 @@ "toolName": "EvoMaster", "toolVersion": "unknown", "creationTime": "2025-04-09T19:31:54.258Z", + "executionTimeInSeconds": 6780000, "faults": { "totalNumber": 529, "foundFaults": [ @@ -2970,7 +2971,7 @@ }, "problemDetails": { "rest": { - "totalHttpCalls": 130, + "outputHttpCalls": 25, "endpointIds": [ "GET:/app/api/assignments", "POST:/app/api/assignments", @@ -3951,7 +3952,8 @@ 200 ] } - ] + ], + "evaluatedHttpCalls": 132 } }, "totalTests": 127, @@ -4867,4 +4869,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/web-report/src/assets/info.json b/web-report/src/assets/info.json index c95eb28..36c02cb 100644 --- a/web-report/src/assets/info.json +++ b/web-report/src/assets/info.json @@ -1,6 +1,8 @@ { "numberOfEndpoints": "Number of endpoints (verb:path) in the API.", - "numberOfHttpCalls": "Total number of HTTP calls in the generated test suites.", + "outputHttpCalls": "Total number of HTTP calls contained in all generated test cases - a single test case may include multiple calls (e.g., a POST followed by a GET and then a DELETE).", + "evaluatedHttpCalls": "Total number of all HTTP calls made during the entire fuzzing session - in long runs, this can reach millions, while the generated tests include only a small subset of these calls.", + "executionTimeInSeconds": "For how long, in seconds, the tool was running in total.", "codeNumberIdentifiers": "Code number identifiers for detected fault types", "identifierName": "Identifier name for the fault type.", "testFilesLocated": "{numberOfTestCases} test cases are located in {fileName}", @@ -15,4 +17,4 @@ "creationDate": "Date when the report was generated.", "toolNameVersion": "Name and version of the tool that generated the report.", "schemaVersion": "Version of the schema used for the report." -} \ No newline at end of file +} diff --git a/web-report/src/components/Dashboard.tsx b/web-report/src/components/Dashboard.tsx index 6c41ade..f5b1f2e 100644 --- a/web-report/src/components/Dashboard.tsx +++ b/web-report/src/components/Dashboard.tsx @@ -114,7 +114,9 @@ export const Dashboard: React.FC = () => { + faults={data.faults} + executionTimeInSeconds={data.executionTimeInSeconds} + /> diff --git a/web-report/src/components/GeneratedTests.tsx b/web-report/src/components/GeneratedTests.tsx index 8a4db65..b9c3d89 100644 --- a/web-report/src/components/GeneratedTests.tsx +++ b/web-report/src/components/GeneratedTests.tsx @@ -1,5 +1,5 @@ import {Card} from "@/components/ui/card.tsx"; -import {Target} from "lucide-react"; +import {Target, Clock, FileText, TestTube, Network, CheckCircle, FolderOpen} from "lucide-react"; import type React from "react"; import {getFileColor, getText} from "@/lib/utils"; import info from "@/assets/info.json"; @@ -11,57 +11,151 @@ interface IGeneratedTests { fileName: string, numberOfTestCases: number }> - totalHttpCalls?: number + outputHttpCalls?: number + evaluatedHttpCalls?: number + executionTimeInSeconds?: number } -export const GeneratedTests: React.FC = ({totalTests, testFiles, totalHttpCalls}) => ( +const formatExecutionTime = (totalSeconds?: number) => { + if (totalSeconds === undefined || totalSeconds === null) return null; + + const days = Math.floor(totalSeconds / 86400); + const hours = Math.floor((totalSeconds % 86400) / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = Math.floor(totalSeconds % 60); + + return { days, hours, minutes, seconds }; +}; + +export const GeneratedTests: React.FC = ({totalTests, testFiles, outputHttpCalls, evaluatedHttpCalls, executionTimeInSeconds}) => { + const timeBreakdown = formatExecutionTime(executionTimeInSeconds); + + return (
-
+
- # Generated Test Files: +
+
+
+ +
+
+
Generated Test Files
+
{testFiles.length}
+
+
+
- {testFiles.length} -
-
+ - # Generated Tests Cases: +
+
+
+ +
+
+
Generated Test Cases
+
{totalTests}
+
+
+
- {totalTests} -
-
- - # HTTP Calls: + + +
+
+
+ +
+
+
Output HTTP Calls
+
{outputHttpCalls}
+
+
+
+
+ + +
+
+
+ +
+
+
Evaluated HTTP Calls
+
{evaluatedHttpCalls}
+
+
+
- {totalHttpCalls}
+ {timeBreakdown && ( +
+ +
+ + Execution Time +
+
+
+
+
{timeBreakdown.days}
+
Days
+
+
+
{timeBreakdown.hours}
+
Hours
+
+
+
{timeBreakdown.minutes}
+
Minutes
+
+
+
{timeBreakdown.seconds}
+
Seconds
+
+
+
+ )} -
-
Test Files
-
- { - testFiles.length > 0 ? ( - testFiles.map((file, index) => ( -
-
- +
+
+ + Test Files +
+
+ { + testFiles.length > 0 ? ( + testFiles.map((file, index) => ( + - {file.fileName} (# {file.numberOfTestCases}) +
+
+
+ {file.fileName} +
+
+ {file.numberOfTestCases} +
+
-
- )) - ) : ( -
No test files generated.
- ) - } + )) + ) : ( +
No test files generated.
+ ) + } +
-) \ No newline at end of file + ); +}; diff --git a/web-report/src/pages/Overview.tsx b/web-report/src/pages/Overview.tsx index e05119c..d447227 100644 --- a/web-report/src/pages/Overview.tsx +++ b/web-report/src/pages/Overview.tsx @@ -12,9 +12,10 @@ interface IOverviewType { numberOfTestCases: number }>, faults: Faults + executionTimeInSeconds: number; } -export const Overview: React.FC = ({rest, testCases, testFiles, faults}) => { +export const Overview: React.FC = ({rest, testCases, testFiles, faults, executionTimeInSeconds}) => { return (
{/* Left Panel */} @@ -22,10 +23,14 @@ export const Overview: React.FC = ({rest, testCases, testFiles, f {/* Right Panel */}
{/* Generated Tests */} - + {/* Faults */}
) -} \ No newline at end of file +} diff --git a/web-report/src/types/GeneratedTypes.tsx b/web-report/src/types/GeneratedTypes.tsx index 41d430f..a6d2a0a 100644 --- a/web-report/src/types/GeneratedTypes.tsx +++ b/web-report/src/types/GeneratedTypes.tsx @@ -56,6 +56,10 @@ export interface WebFuzzingCommonsReport { * Information on each generated test case. */ testCases: TestCase[]; + /** + * For how long, in seconds, the tool was running in total. + */ + executionTimeInSeconds: number; /** * Extra, optional coverage information, collected by different tools. */ @@ -101,9 +105,13 @@ export interface FaultCategoryId { } export interface RESTReport { /** - * Total number of HTTP calls made in all the test cases. A test case could contain several HTTP calls, e.g., a POST followed by a GET and then a DELETE. + * Total number of HTTP calls made in all the generated test cases given as output. A test case could contain several HTTP calls, e.g., a POST followed by a GET and then a DELETE. + */ + outputHttpCalls: number; + /** + * Total number of all HTTP calls made during the whole fuzzing session. If the fuzzing was left running for hours, millions of HTTP could have been made. The output generated tests will only contain a tiny subset of these evaluated calls. */ - totalHttpCalls: number; + evaluatedHttpCalls: number; /** * Unique ids of all the endpoints in the tested API. */ diff --git a/web-report/src/types/GeneratedTypesZod.ts b/web-report/src/types/GeneratedTypesZod.ts index 941f69a..279beff 100644 --- a/web-report/src/types/GeneratedTypesZod.ts +++ b/web-report/src/types/GeneratedTypesZod.ts @@ -44,7 +44,8 @@ export const coverageCriterionSchema = z.record(z.unknown()).and( export const rESTReportSchema = z.record(z.unknown()).and( z.object({ - totalHttpCalls: z.number(), + outputHttpCalls: z.number(), + evaluatedHttpCalls: z.number(), endpointIds: z.array(operationIdSchema), coveredHttpStatus: z.array(coveredEndpointSchema), }), @@ -89,6 +90,7 @@ export const webFuzzingCommonsReportSchema = z.record(z.unknown()).and( totalTests: z.number(), testFilePaths: z.array(testFilePathSchema), testCases: z.array(testCaseSchema), + executionTimeInSeconds: z.number(), extra: z.array(coverageSchema).optional().nullable(), }), );