1
0
Fork 0
This commit is contained in:
Atridad Lahiji 2024-01-17 00:56:04 -07:00
parent c888c47742
commit 0a2d95652f
No known key found for this signature in database
5 changed files with 165 additions and 36 deletions

View file

@ -6,57 +6,44 @@ import (
"net/http"
"os"
"path/filepath"
"sync"
"time"
)
// PerformanceMetrics holds the metrics for performance evaluation.
type PerformanceMetrics struct {
mu sync.Mutex // Protects the metrics
totalRequests int32
totalResponses int32
totalLatency time.Duration
maxLatency time.Duration
minLatency time.Duration
responseCounters map[int]int32
}
// Initialize the metrics with default values.
var metrics = PerformanceMetrics{
minLatency: time.Duration(math.MaxInt64),
responseCounters: make(map[int]int32),
MinLatency: time.Duration(math.MaxInt64),
ResponseCounters: make(map[int]int32),
}
// updateMetrics updates the performance metrics.
func UpdateMetrics(duration time.Duration, resp *http.Response, second int) {
metrics.mu.Lock()
defer metrics.mu.Unlock()
metrics.Mu.Lock()
defer metrics.Mu.Unlock()
metrics.totalRequests++
metrics.totalLatency += duration
if duration > metrics.maxLatency {
metrics.maxLatency = duration
metrics.TotalRequests++
metrics.TotalLatency += duration
if duration > metrics.MaxLatency {
metrics.MaxLatency = duration
}
if duration < metrics.minLatency {
metrics.minLatency = duration
if duration < metrics.MinLatency {
metrics.MinLatency = duration
}
if resp.StatusCode == http.StatusOK {
metrics.totalResponses++
metrics.responseCounters[second]++
metrics.TotalResponses++
metrics.ResponseCounters[second]++
}
}
// calculateAndPrintMetrics calculates and prints the performance metrics.
func CalculateAndPrintMetrics(startTime time.Time, requestsPerSecond float64, endpoint string, verb string) {
averageLatency := time.Duration(0)
if metrics.totalRequests > 0 {
averageLatency = metrics.totalLatency / time.Duration(metrics.totalRequests)
if metrics.TotalRequests > 0 {
averageLatency = metrics.TotalLatency / time.Duration(metrics.TotalRequests)
}
totalDuration := time.Since(startTime).Seconds()
totalResponses := int32(0)
for _, count := range metrics.responseCounters {
for _, count := range metrics.ResponseCounters {
totalResponses += count
}
@ -65,11 +52,11 @@ func CalculateAndPrintMetrics(startTime time.Time, requestsPerSecond float64, en
results += fmt.Sprintf("HTTP Verb: %s\n", verb)
results += fmt.Sprintln("--------------------")
results += fmt.Sprintln("Performance Metrics:")
results += fmt.Sprintf("Total Requests Sent: %d\n", metrics.totalRequests)
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("Max Latency: %s\n", metrics.MaxLatency)
results += fmt.Sprintf("Min Latency: %s\n", metrics.MinLatency)
results += fmt.Sprintf("Requests Per Second (Sent): %.2f\n", float64(requestsPerSecond))
results += fmt.Sprintf("Responses Per Second (Received): %.2f\n", float64(totalResponses)/totalDuration)

73
lib/metrics_test.go Normal file
View file

@ -0,0 +1,73 @@
package lib_test
import (
"loadr/lib"
"reflect"
"testing"
"time"
)
func comparePerformanceMetrics(a, b *lib.PerformanceMetrics) bool {
return a.TotalRequests == b.TotalRequests &&
a.TotalResponses == b.TotalResponses &&
a.TotalLatency == b.TotalLatency &&
a.MaxLatency == b.MaxLatency &&
a.MinLatency == b.MinLatency &&
reflect.DeepEqual(a.ResponseCounters, b.ResponseCounters)
}
func TestCalculateAndPrintMetrics(t *testing.T) {
// Define test cases.
tests := []struct {
name string
startTime time.Time
requestsPerSecond float64
endpoint string
verb string
expectedMetrics lib.PerformanceMetrics
}{
{
name: "Test 1",
startTime: time.Now().Add(-1 * time.Second), // 1 second ago
requestsPerSecond: 1.0,
endpoint: "http://localhost",
verb: "GET",
expectedMetrics: lib.PerformanceMetrics{
TotalRequests: 1,
TotalResponses: 1,
TotalLatency: 1 * time.Second,
MaxLatency: 1 * time.Second,
MinLatency: 1 * time.Second,
ResponseCounters: map[int]int32{1: 1},
},
},
// Add more test cases as needed.
}
for i := range tests {
tt := &tests[i]
t.Run(tt.name, func(t *testing.T) {
// Reset the metrics before each test
metrics := lib.PerformanceMetrics{}
// Mock the system behavior
metrics.TotalRequests++
metrics.ResponseCounters = make(map[int]int32)
metrics.TotalResponses++
metrics.TotalLatency += 1 * time.Second
metrics.MaxLatency = 1 * time.Second
metrics.MinLatency = 1 * time.Second
metrics.ResponseCounters[1]++
// Call the function
lib.CalculateAndPrintMetrics(tt.startTime, tt.requestsPerSecond, tt.endpoint, tt.verb)
// Check if the metrics are correct
if !comparePerformanceMetrics(&metrics, &tt.expectedMetrics) {
t.Errorf("CalculateAndPrintMetrics() = TotalRequests: %v, TotalResponses: %v, TotalLatency: %v, MaxLatency: %v, MinLatency: %v, ResponseCounters: %v, want TotalRequests: %v, TotalResponses: %v, TotalLatency: %v, MaxLatency: %v, MinLatency: %v, ResponseCounters: %v",
metrics.TotalRequests, metrics.TotalResponses, metrics.TotalLatency, metrics.MaxLatency, metrics.MinLatency, metrics.ResponseCounters,
tt.expectedMetrics.TotalRequests, tt.expectedMetrics.TotalResponses, tt.expectedMetrics.TotalLatency, tt.expectedMetrics.MaxLatency, tt.expectedMetrics.MinLatency, tt.expectedMetrics.ResponseCounters)
}
})
}
}

View file

@ -13,11 +13,7 @@ import (
// Global HTTP client used for making requests.
var client = &http.Client{}
type RequestError struct {
Verb string
URL string
Err error
}
func (e *RequestError) Error() string {
return fmt.Sprintf("error making %s request to %s: %v", e.Verb, e.URL, e.Err)

50
lib/requests_test.go Normal file
View file

@ -0,0 +1,50 @@
package lib_test
import (
"loadr/lib"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestSendRequests(t *testing.T) {
// Create a test server that responds with a 200 status.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
tests := []struct {
name string
url string
bearerToken string
requestType string
jsonData []byte
maxRequests int
requestsPerSecond float64
}{
{
name: "Test 1",
url: ts.URL,
bearerToken: "testToken",
requestType: "GET",
jsonData: []byte(`{"key":"value"}`),
maxRequests: 5,
requestsPerSecond: 1.0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
start := time.Now()
lib.SendRequests(tt.url, tt.bearerToken, tt.requestType, tt.jsonData, tt.maxRequests, tt.requestsPerSecond)
elapsed := time.Since(start)
// Check if the requests were sent within the expected time frame.
if elapsed > time.Duration(float64(tt.maxRequests)*1.5)*time.Second {
t.Errorf("SendRequests() took too long, got: %v, want: less than %v", elapsed, time.Duration(float64(tt.maxRequests)*1.5)*time.Second)
}
})
}
}

23
lib/types.go Normal file
View file

@ -0,0 +1,23 @@
package lib
import (
"sync"
"time"
)
// PerformanceMetrics holds the metrics for performance evaluation.
type PerformanceMetrics struct {
Mu sync.Mutex // Protects the metrics
TotalRequests int32
TotalResponses int32
TotalLatency time.Duration
MaxLatency time.Duration
MinLatency time.Duration
ResponseCounters map[int]int32
}
type RequestError struct {
Verb string
URL string
Err error
}