package lib import ( "fmt" "math" "net/http" "os" "path/filepath" "strings" "time" ) // Global metrics instance to track performance during load testing var metrics = PerformanceMetrics{ // Initialize MinLatency to the maximum possible duration MinLatency: time.Duration(math.MaxInt64), // Initialize response counters map ResponseCounters: make(map[int]int32), } // UpdateMetrics synchronously updates performance metrics for each request // - duration: time taken to complete the request // - resp: HTTP response from the request // - second: elapsed seconds since the start of the test func UpdateMetrics(duration time.Duration, resp *http.Response, second int) { metrics.Mu.Lock() defer metrics.Mu.Unlock() // Increment total requests metrics.TotalRequests++ // Add current request's latency to total metrics.TotalLatency += duration // Update maximum latency if current duration is higher if duration > metrics.MaxLatency { metrics.MaxLatency = duration } // Update minimum latency if current duration is lower if duration < metrics.MinLatency { metrics.MinLatency = duration } // Track successful responses if resp.StatusCode == http.StatusOK { metrics.TotalResponses++ metrics.ResponseCounters[second]++ } // Debug log of current metrics fmt.Printf("Current metrics - Total Requests: %d, Total Responses: %d\n", metrics.TotalRequests, metrics.TotalResponses) } // CalculateAndPrintMetrics generates a comprehensive report of load test performance // Parameters: // - startTime: when the load test began // - requestsPerSecond: target request rate // - endpoint: URL being tested // - patterns: request patterns used in the test func CalculateAndPrintMetrics(startTime time.Time, requestsPerSecond float64, endpoint string, patterns []RequestPattern) { // Small delay to ensure all metrics are captured time.Sleep(100 * time.Millisecond) metrics.Mu.Lock() defer metrics.Mu.Unlock() // Calculate average latency averageLatency := time.Duration(0) if metrics.TotalRequests > 0 { averageLatency = metrics.TotalLatency / time.Duration(metrics.TotalRequests) } // Calculate total test duration and total responses totalDuration := time.Since(startTime).Seconds() totalResponses := int32(0) for _, count := range metrics.ResponseCounters { totalResponses += count } // Ensure MinLatency is not left at its initial max value if metrics.MinLatency == time.Duration(math.MaxInt64) { metrics.MinLatency = 0 } // Build detailed results string results := fmt.Sprintf("Load Test Report\n") results += fmt.Sprintf("=============\n\n") // Report endpoint and request pattern results += fmt.Sprintf("Endpoint: %s\n", endpoint) results += fmt.Sprintf("Pattern: ") for i, p := range patterns { if i > 0 { results += " → " } var patternDesc string if p.Percentage > 0 && p.Percentage < 100 { // Probabilistic pattern (e.g., "20%p80%g") patternDesc = fmt.Sprintf("%.0f%%%s", p.Percentage, strings.ToLower(p.Verb[:1])) } else { // Simple or sequential pattern (e.g., "5p", "3g", "1p5g") patternDesc = fmt.Sprintf("%d%s", p.Sequence, strings.ToLower(p.Verb[:1])) } results += patternDesc } results += "\n\n" // Detailed performance metrics results += fmt.Sprintf("Performance Metrics\n") results += fmt.Sprintf("-----------------\n") results += fmt.Sprintf("Total Requests Sent: %d\n", metrics.TotalRequests) results += fmt.Sprintf("Total Responses Received: %d\n", totalResponses) results += fmt.Sprintf("Average Latency: %s\n", averageLatency) results += fmt.Sprintf("Max Latency: %s\n", metrics.MaxLatency) results += fmt.Sprintf("Min Latency: %s\n", metrics.MinLatency) results += fmt.Sprintf("Requests/sec (Target): %.2f\n", requestsPerSecond) results += fmt.Sprintf("Requests/sec (Actual): %.2f\n", float64(metrics.TotalRequests)/totalDuration) results += fmt.Sprintf("Responses/sec: %.2f\n", float64(totalResponses)/totalDuration) // Print and save the report fmt.Println(results) saveReport(results) } // saveReport writes the load test results to a timestamped file in the .reports directory // Parameters: // - results: formatted results string to be saved func saveReport(results string) { // Ensure .reports directory exists resultsDir := ".reports" os.MkdirAll(resultsDir, os.ModePerm) // Create a unique filename based on current timestamp resultsFile := filepath.Join(resultsDir, fmt.Sprintf("%d.txt", time.Now().Unix())) // Write results to file if err := os.WriteFile(resultsFile, []byte(results), 0644); err != nil { fmt.Println("Error saving report:", err) return } fmt.Println("Report saved:", resultsFile) }