Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions test/e2e/features/story_microshift.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ Feature: Microshift test stories
And setting config property "persistent-volume-size" to value "20" succeeds
And ensuring network mode user
And executing single crc setup command succeeds
And get cpu data "Before start"
And get memory data "Before start"
And starting CRC with default bundle succeeds
And get cpu data "After start"
And get memory data "After start"
And ensuring oc command is available
And ensuring microshift cluster is fully operational
Expand All @@ -20,8 +17,9 @@ Feature: Microshift test stories

# End-to-end health check

@microshift @testdata @linux @windows @darwin @cleanup
@microshift @testdata @linux @windows @darwin @cleanup @performance
Scenario: Start and expose a basic HTTP service and check after restart
And record timestamp "deployment"
Given executing "oc create namespace testproj" succeeds
And executing "oc config set-context --current --namespace=testproj" succeeds
When executing "oc apply -f httpd-example.yaml" succeeds
Expand All @@ -36,13 +34,13 @@ Feature: Microshift test stories
When executing "oc expose svc httpd-example" succeeds
Then stdout should contain "httpd-example exposed"
When with up to "20" retries with wait period of "5s" http response from "http://httpd-example-testproj.apps.crc.testing" has status code "200"
And get cpu data "After deployment"
And get memory data "After deployment"
Then executing "curl -s http://httpd-example-testproj.apps.crc.testing" succeeds
And stdout should contain "Hello CRC!"
And record timestamp "stop"
When executing "crc stop" succeeds
And get cpu data "After stop"
And get memory data "After stop"
And record timestamp "start again"
And starting CRC with default bundle succeeds
And checking that CRC is running
And with up to "4" retries with wait period of "1m" http response from "http://httpd-example-testproj.apps.crc.testing" has status code "200"
Expand Down
10 changes: 5 additions & 5 deletions test/e2e/features/story_openshift.feature
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ Feature: 4 Openshift stories

# End-to-end health check

@darwin @linux @windows @testdata @story_health @needs_namespace
@darwin @linux @windows @testdata @story_health @needs_namespace @performance
Scenario: Overall cluster health
Given executing "oc new-project testproj" succeeds
And get cpu data "After start"
And get memory data "After start"
And record timestamp "deployment"
When executing "oc apply -f httpd-example.yaml" succeeds
And executing "oc rollout status deployment httpd-example" succeeds
Then stdout should contain "successfully rolled out"
Expand All @@ -25,14 +25,14 @@ Feature: 4 Openshift stories
Then stdout should contain "httpd-example exposed"
When executing "oc expose svc httpd-example" succeeds
Then stdout should contain "httpd-example exposed"
And get cpu data "After deployment"
And get memory data "After deployment"
When with up to "20" retries with wait period of "5s" http response from "http://httpd-example-testproj.apps-crc.testing" has status code "200"
Then executing "curl -s http://httpd-example-testproj.apps-crc.testing" succeeds
And stdout should contain "Hello CRC!"
And get memory data "After deployment"
And record timestamp "stop"
When executing "crc stop" succeeds
And get cpu data "After stop"
And get memory data "After stop"
And record timestamp "start again"
And starting CRC with default bundle succeeds
And checking that CRC is running
And with up to "4" retries with wait period of "1m" http response from "http://httpd-example-testproj.apps-crc.testing" has status code "200"
Expand Down
123 changes: 123 additions & 0 deletions test/e2e/testsuite/performance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package testsuite

import (
"context"
"fmt"
"os"
"path/filepath"
"sync"
"time"

"github.com/crc-org/crc/v2/test/extended/util"
"github.com/shirou/gopsutil/v4/cpu"
)

type Monitor struct {
cancelFunc context.CancelFunc
isRunning bool
mu sync.Mutex
wg sync.WaitGroup
interval time.Duration
}

func NewMonitor(interval time.Duration) *Monitor {
return &Monitor{
interval: interval,
}
}

func (m *Monitor) Start() error {
m.mu.Lock()
defer m.mu.Unlock()

if m.isRunning {
return fmt.Errorf("The collector is running")
}

fmt.Printf("Attempt to start CPU collector, interval: %s", m.interval)

// create a context.WithCancel
ctx, cancel := context.WithCancel(context.Background())
m.cancelFunc = cancel
m.isRunning = true

// stary goroutine
m.wg.Add(1)
go m.collectLoop(ctx)

fmt.Println("CPU collector has been successfully started")
return nil
}
Comment on lines +29 to +50
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add missing newline in log message.

Line 37 uses fmt.Printf without a trailing newline, causing the subsequent message on line 48 to appear on the same line.

Apply this diff:

 	if m.isRunning {
 		return fmt.Errorf("The collector is running")
 	}
 
-	fmt.Printf("Attempt to start CPU collector, interval: %s", m.interval)
+	fmt.Printf("Attempt to start CPU collector, interval: %s\n", m.interval)
 
 	//  create a context.WithCancel
 	ctx, cancel := context.WithCancel(context.Background())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (m *Monitor) Start() error {
m.mu.Lock()
defer m.mu.Unlock()
if m.isRunning {
return fmt.Errorf("The collector is running")
}
fmt.Printf("Attempt to start CPU collector, interval: %s", m.interval)
// create a context.WithCancel
ctx, cancel := context.WithCancel(context.Background())
m.cancelFunc = cancel
m.isRunning = true
// stary goroutine
m.wg.Add(1)
go m.collectLoop(ctx)
fmt.Println("CPU collector has been successfully started")
return nil
}
func (m *Monitor) Start() error {
m.mu.Lock()
defer m.mu.Unlock()
if m.isRunning {
return fmt.Errorf("The collector is running")
}
fmt.Printf("Attempt to start CPU collector, interval: %s\n", m.interval)
// create a context.WithCancel
ctx, cancel := context.WithCancel(context.Background())
m.cancelFunc = cancel
m.isRunning = true
// stary goroutine
m.wg.Add(1)
go m.collectLoop(ctx)
fmt.Println("CPU collector has been successfully started")
return nil
}
🤖 Prompt for AI Agents
In test/e2e/testsuite/performance.go around lines 29 to 50, the fmt.Printf call
on line 37 lacks a trailing newline so the next println appears on the same
line; update that call to include a newline (e.g., add "\n" to the format string
or replace with fmt.Println/Printf that ends with "\n") so the "CPU collector
has been successfully started" message appears on its own line.


func (m *Monitor) Stop() error {
m.mu.Lock()
defer m.mu.Unlock()

if !m.isRunning {
return fmt.Errorf("The collector is not running")
}
if m.cancelFunc != nil {
m.cancelFunc()
}
m.isRunning = false
m.wg.Wait()
fmt.Println("CPU collector has sent a stop signal")
// may need wait a while to stop
return nil
}

func (m *Monitor) collectLoop(ctx context.Context) {
defer m.wg.Done()

fmt.Println("--> collect goroutine start...")
calcInterval := m.interval

for {
// 1. check Context whether be cancled
select {
case <-ctx.Done():
fmt.Println("<-- collect goroutine receive stop signal")
return // exit goroutine
default:
// continue collect data
}

// 2. collect data
totalPercent, err := cpu.Percent(calcInterval, false)
// no need to sleep, calcInterval automatically do it

if err != nil {
fmt.Printf("Error: fail to collect CPU data: %v", err)
time.Sleep(1 * time.Second)
continue
}

if len(totalPercent) > 0 {
data := fmt.Sprintf("[%s], cpu percent: %.2f%%\n",
time.Now().Format("15:04:05"), totalPercent[0])
wd, err := os.Getwd()
if err != nil {
fmt.Printf("Error: failed to get working directory: %v\n", err)
continue
}
file := filepath.Join(wd, "../test-results/cpu-consume.txt")
err = util.WriteToFile(data, file)
if err != nil {
fmt.Printf("Error: fail to write to %s: %v", file, err)
}
}
Comment on lines +69 to +108
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add missing newlines in error messages.

Lines 90 and 106 use fmt.Printf without trailing newlines, which can cause log messages to be concatenated.

Apply this diff:

 		// 2. collect data
 		totalPercent, err := cpu.Percent(calcInterval, false)
 		// no need to sleep, calcInterval automatically do it
 
 		if err != nil {
-			fmt.Printf("Error: fail to collect CPU data: %v", err)
+			fmt.Printf("Error: fail to collect CPU data: %v\n", err)
 			time.Sleep(1 * time.Second)
 			continue
 		}
 
 		if len(totalPercent) > 0 {
 			data := fmt.Sprintf("[%s], cpu percent: %.2f%%\n",
 				time.Now().Format("15:04:05"), totalPercent[0])
 			wd, err := os.Getwd()
 			if err != nil {
 				fmt.Printf("Error: failed to get working directory: %v\n", err)
 				continue
 			}
 			file := filepath.Join(wd, "../test-results/cpu-consume.txt")
 			err = util.WriteToFile(data, file)
 			if err != nil {
-				fmt.Printf("Error: fail to write to %s: %v", file, err)
+				fmt.Printf("Error: fail to write to %s: %v\n", file, err)
 			}
 		}
🤖 Prompt for AI Agents
In test/e2e/testsuite/performance.go around lines 69 to 108, the two fmt.Printf
calls (around lines 90 and 106) are missing trailing newlines which can cause
log messages to run together; update those calls to include a terminating "\n"
in their format strings (e.g., change "%v" to "%v\n") so each error message ends
with a newline and logs remain separated and readable.

/*
vMem, err := mem.VirtualMemory()
if err != nil {
fmt.Printf("Error: failed to collect memory data: %v", err)
continue
}
memoryused := vMem.Used / 1024 / 1024
data := fmt.Sprintf("[%s], MemoryUsed(MB): %d\n" ,
time.Now().Format("15:04:05"), memoryused)
wd, _ := os.Getwd()
file := filepath.Join(wd, "../test-results/memory-consume.txt")
util.WriteToFile(data, file)
*/
}
}
57 changes: 35 additions & 22 deletions test/e2e/testsuite/testsuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
crcCmd "github.com/crc-org/crc/v2/test/extended/crc/cmd"
"github.com/crc-org/crc/v2/test/extended/util"
"github.com/cucumber/godog"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/mem"
"github.com/spf13/pflag"
)
Expand Down Expand Up @@ -169,6 +168,7 @@ func InitializeTestSuite(tctx *godog.TestSuiteContext) {
}

func InitializeScenario(s *godog.ScenarioContext) {
monitor := NewMonitor(1 * time.Second)

s.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {

Expand Down Expand Up @@ -264,12 +264,17 @@ func InitializeScenario(s *godog.ScenarioContext) {
}
}

if tag.Name == "@story_health" {
if err := getCPUdata("Before start"); err != nil {
fmt.Printf("Failed to collect CPU data: %v\n", err)
if tag.Name == "@performance" {
if err := monitor.Start(); err != nil {
fmt.Printf("Failed to start monitor: %v\n", err)
}
if err := getMemoryData("Before start"); err != nil {
fmt.Printf("Failed to collect memory data: %v\n", err)
err := getTimestamp("start")
if err != nil {
fmt.Printf("Failed to get finish getTimestamp: %v\n", err)
}
err = getMemoryData("Before start")
if err != nil {
fmt.Printf("Failed to get memory data: %v\n", err)
}
}
}
Expand Down Expand Up @@ -398,6 +403,17 @@ func InitializeScenario(s *godog.ScenarioContext) {
}
}

if tag.Name == "@performance" {
err := getTimestamp("finish")
if err != nil {
fmt.Printf("Failed to get finish getTimestamp: %v\n", err)
}
if err := monitor.Stop(); err != nil {
fmt.Printf("Failed to stop monitoring: %v\n", err)
}
fmt.Printf("Collection has stopped. Wait for 5 seconds to confirm that the collection task will no longer output data\n")
time.Sleep(5 * time.Second)
}
}

return ctx, nil
Expand Down Expand Up @@ -579,8 +595,8 @@ func InitializeScenario(s *godog.ScenarioContext) {
EnsureApplicationIsAccessibleViaNodePort)
s.Step(`^persistent volume of size "([^"]*)"GB exists$`,
EnsureVMPartitionSizeCorrect)
s.Step(`^get cpu data "([^"]*)"`,
getCPUdata)
s.Step(`^record timestamp "([^"]*)"`,
getTimestamp)
s.Step(`^get memory data "([^"]*)"`,
getMemoryData)

Expand Down Expand Up @@ -1320,20 +1336,6 @@ func deserializeListBlockDeviceCommandOutputToExtractPVSize(lsblkOutput string)
return diskSize - (lvmSize + 1), nil
}

func getCPUdata(content string) error {
cpuData, err := cpu.Percent(0, false)
if err != nil {
return fmt.Errorf("failed to get CPU data: %v", err)
}
if len(cpuData) == 0 {
return fmt.Errorf("no CPU data available")
}
data := fmt.Sprintf("%s: %.2f%%\n", content, cpuData)
wd, _ := os.Getwd()
file := filepath.Join(wd, "../test-results/cpu-consume.txt")
return util.WriteToFile(data, file)
}

func getMemoryData(content string) error {
v, err := mem.VirtualMemory()
if err != nil {
Expand All @@ -1351,3 +1353,14 @@ func getMemoryData(content string) error {
file := filepath.Join(wd, "../test-results/memory-consume.txt")
return util.WriteToFile(data, file)
}

func getTimestamp(content string) error {
data := fmt.Sprintf("[%s], %s\n",
time.Now().Format("15:04:05"), content)
wd, err := os.Getwd()
if err != nil {
fmt.Printf("failed to get working directory: %v\n", err)
}
file := filepath.Join(wd, "../test-results/time-stamp.txt")
return util.WriteToFile(data, file)
}
Loading