diff --git a/cmd/exporter/exporter0_test.go b/cmd/exporter/exporter0_test.go deleted file mode 100644 index 8c04b6b98c..0000000000 --- a/cmd/exporter/exporter0_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package your_package_name - -import ( - "flag" - "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = ginkgo.Describe("AppConfig", func() { - var cfg *AppConfig - - ginkgo.BeforeEach(func() { - // Reset the command-line flags before each test - flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError) - cfg = newAppConfig() - }) - - ginkgo.It("should initialize with default values", func() { - Expect(cfg.BaseDir).To(Equal(config.BaseDir)) - Expect(cfg.Address).To(Equal("0.0.0.0:8888")) - Expect(cfg.MetricsPath).To(Equal("/metrics")) - Expect(cfg.EnableGPU).To(BeFalse()) - Expect(cfg.EnableEBPFCgroupID).To(BeTrue()) - Expect(cfg.ExposeHardwareCounterMetrics).To(BeTrue()) - Expect(cfg.EnableMSR).To(BeFalse()) - Expect(cfg.Kubeconfig).To(BeEmpty()) - Expect(cfg.ApiserverEnabled).To(BeTrue()) - Expect(cfg.RedfishCredFilePath).To(BeEmpty()) - Expect(cfg.ExposeEstimatedIdlePower).To(BeFalse()) - Expect(cfg.MachineSpecFilePath).To(BeEmpty()) - Expect(cfg.DisablePowerMeter).To(BeFalse()) - Expect(cfg.TLSFilePath).To(BeEmpty()) - }) - - ginkgo.It("should override default values with command-line flags", func() { - // Set command-line flags - flag.Set("config-dir", "/custom/config/dir") - flag.Set("address", "127.0.0.1:8080") - flag.Set("metrics-path", "/custom/metrics") - flag.Set("enable-gpu", "true") - flag.Set("enable-cgroup-id", "false") - flag.Set("expose-hardware-counter-metrics", "false") - flag.Set("enable-msr", "true") - flag.Set("kubeconfig", "/custom/kubeconfig") - flag.Set("apiserver", "false") - flag.Set("redfish-cred-file-path", "/custom/redfish/cred") - flag.Set("expose-estimated-idle-power", "true") - flag.Set("machine-spec", "/custom/machine/spec") - flag.Set("disable-power-meter", "true") - flag.Set("web.config.file", "/custom/tls/config") - - // Parse the flags - flag.Parse() - - // Reinitialize the config with the new flag values - cfg = newAppConfig() - - // Verify that the values are overridden - Expect(cfg.BaseDir).To(Equal("/custom/config/dir")) - Expect(cfg.Address).To(Equal("127.0.0.1:8080")) - Expect(cfg.MetricsPath).To(Equal("/custom/metrics")) - Expect(cfg.EnableGPU).To(BeTrue()) - Expect(cfg.EnableEBPFCgroupID).To(BeFalse()) - Expect(cfg.ExposeHardwareCounterMetrics).To(BeFalse()) - Expect(cfg.EnableMSR).To(BeTrue()) - Expect(cfg.Kubeconfig).To(Equal("/custom/kubeconfig")) - Expect(cfg.ApiserverEnabled).To(BeFalse()) - Expect(cfg.RedfishCredFilePath).To(Equal("/custom/redfish/cred")) - Expect(cfg.ExposeEstimatedIdlePower).To(BeTrue()) - Expect(cfg.MachineSpecFilePath).To(Equal("/custom/machine/spec")) - Expect(cfg.DisablePowerMeter).To(BeTrue()) - Expect(cfg.TLSFilePath).To(Equal("/custom/tls/config")) - }) -}) \ No newline at end of file diff --git a/cmd/exporter/exporter1_test.go b/cmd/exporter/exporter1_test.go deleted file mode 100644 index c3ea102736..0000000000 --- a/cmd/exporter/exporter1_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "net/http" - "net/http/httptest" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("HealthProbe", func() { - var ( - w *httptest.ResponseRecorder - req *http.Request - ) - - BeforeEach(func() { - // Initialize the response recorder and request before each test - w = httptest.NewRecorder() - req = httptest.NewRequest("GET", "/health", nil) - }) - - Context("when the health probe is called", func() { - It("should return HTTP status OK", func() { - healthProbe(w, req) - - Expect(w.Code).To(Equal(http.StatusOK)) - }) - - It("should return 'ok' in the response body", func() { - healthProbe(w, req) - - Expect(w.Body.String()).To(Equal("ok")) - }) - }) -}) \ No newline at end of file diff --git a/cmd/exporter/exporter2_test.go b/cmd/exporter/exporter2_test.go deleted file mode 100644 index ecd5911846..0000000000 --- a/cmd/exporter/exporter2_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "flag" - "os" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "k8s.io/klog/v2" -) - -var _ = Describe("Main Function", func() { - var ( - originalArgs []string - ) - - BeforeEach(func() { - // Save the original command-line arguments - originalArgs = os.Args - - // Reset the flag command-line arguments - flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) - - // Initialize klog flags - klog.InitFlags(nil) - }) - - AfterEach(func() { - // Restore the original command-line arguments - os.Args = originalArgs - }) - - Context("when the config initialization fails", func() { - It("should log a fatal error and exit", func() { - // Mock the command-line arguments to simulate a failure scenario - os.Args = []string{"cmd", "-base-dir=/invalid/path"} - - // Redirect klog output to a buffer to capture the log messages - klog.SetOutput(GinkgoWriter) - - // Use a defer to recover from the fatal log and prevent the test from exiting - defer func() { - if r := recover(); r != nil { - Expect(r).To(ContainSubstring("Failed to initialize config")) - } - }() - - // Call the main function - main() - }) - }) - - Context("when the config initialization succeeds", func() { - It("should initialize the config without errors", func() { - // Mock the command-line arguments to simulate a success scenario - os.Args = []string{"cmd", "-base-dir=/valid/path"} - - // Redirect klog output to a buffer to capture the log messages - klog.SetOutput(GinkgoWriter) - - // Use a defer to recover from the fatal log and prevent the test from exiting - defer func() { - if r := recover(); r != nil { - Fail("Unexpected fatal log") - } - }() - - // Call the main function - main() - }) - }) -}) \ No newline at end of file diff --git a/cmd/exporter/exporter3_test.go b/cmd/exporter/exporter3_test.go deleted file mode 100644 index bb10125cd1..0000000000 --- a/cmd/exporter/exporter3_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "net/http" - "net/http/httptest" - "strings" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var _ = Describe("RootHandler", func() { - var ( - metricPathConfig string - handler http.HandlerFunc - recorder *httptest.ResponseRecorder - request *http.Request - ) - - BeforeEach(func() { - metricPathConfig = "/metrics" - handler = rootHandler(metricPathConfig) - recorder = httptest.NewRecorder() - request = httptest.NewRequest("GET", "/", nil) - }) - - Context("when the root endpoint is accessed", func() { - It("should return a valid HTML response with the correct metric path", func() { - handler.ServeHTTP(recorder, request) - - Expect(recorder.Code).To(Equal(http.StatusOK)) - Expect(recorder.Body.String()).To(ContainSubstring("Energy Stats Exporter")) - Expect(recorder.Body.String()).To(ContainSubstring("

Energy Stats Exporter

")) - Expect(recorder.Body.String()).To(ContainSubstring(`Metrics`)) - }) - }) - - Context("when the response writing fails", func() { - It("should log an error", func() { - // Mock the response writer to simulate a write failure - failingRecorder := &FailingResponseRecorder{httptest.NewRecorder()} - handler.ServeHTTP(failingRecorder, request) - - // Here you would typically check if the error was logged. - // Since klog is used, you might need to capture logs or mock klog for this test. - // This is a placeholder to indicate where you would check for the error log. - Expect(failingRecorder.Failed).To(BeTrue()) - }) - }) -}) - -// FailingResponseRecorder is a custom ResponseRecorder that fails on Write -type FailingResponseRecorder struct { - *httptest.ResponseRecorder - Failed bool -} - -func (r *FailingResponseRecorder) Write(b []byte) (int, error) { - if strings.Contains(string(b), "Metrics") { - r.Failed = true - return 0, fmt.Errorf("simulated write failure") - } - return r.ResponseRecorder.Write(b) -} \ No newline at end of file diff --git a/cmd/exporter/exporter_suite_test.go b/cmd/exporter/exporter_suite_test.go new file mode 100644 index 0000000000..6b8b879998 --- /dev/null +++ b/cmd/exporter/exporter_suite_test.go @@ -0,0 +1,13 @@ +package main + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestExporter(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Exporter Suite") +} diff --git a/cmd/exporter/exporter_test.go b/cmd/exporter/exporter_test.go new file mode 100644 index 0000000000..7bf65425b3 --- /dev/null +++ b/cmd/exporter/exporter_test.go @@ -0,0 +1,159 @@ +package main + +import ( + "flag" + "fmt" + "net/http" + "net/http/httptest" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/sustainable-computing-io/kepler/pkg/config" +) + +var _ = Describe("AppConfig", func() { + var cfg *AppConfig + + BeforeEach(func() { + cfg = newAppConfig() + // Reset the command-line flags before each test + flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError) + }) + + It("should initialize with default values", func() { + Expect(cfg.BaseDir).To(Equal(config.BaseDir)) + Expect(cfg.Address).To(Equal("0.0.0.0:8888")) + Expect(cfg.MetricsPath).To(Equal("/metrics")) + Expect(cfg.EnableGPU).To(BeFalse()) + Expect(cfg.EnableEBPFCgroupID).To(BeTrue()) + Expect(cfg.ExposeHardwareCounterMetrics).To(BeTrue()) + Expect(cfg.EnableMSR).To(BeFalse()) + Expect(cfg.Kubeconfig).To(BeEmpty()) + Expect(cfg.ApiserverEnabled).To(BeTrue()) + Expect(cfg.RedfishCredFilePath).To(BeEmpty()) + Expect(cfg.ExposeEstimatedIdlePower).To(BeFalse()) + Expect(cfg.MachineSpecFilePath).To(BeEmpty()) + Expect(cfg.DisablePowerMeter).To(BeFalse()) + Expect(cfg.TLSFilePath).To(BeEmpty()) + }) + + It("should override default values with command-line flags", func() { + cfg = newAppConfig() + // Set command-line flags + flag.Set("config-dir", "/custom/config/dir") + flag.Set("address", "127.0.0.1:8080") + flag.Set("metrics-path", "/custom/metrics") + flag.Set("enable-gpu", "true") + flag.Set("enable-cgroup-id", "false") + flag.Set("expose-hardware-counter-metrics", "false") + flag.Set("enable-msr", "true") + flag.Set("kubeconfig", "/custom/kubeconfig") + flag.Set("apiserver", "false") + flag.Set("redfish-cred-file-path", "/custom/redfish/cred") + flag.Set("expose-estimated-idle-power", "true") + flag.Set("machine-spec", "/custom/machine/spec") + flag.Set("disable-power-meter", "true") + flag.Set("web.config.file", "/custom/tls/config") + + // Parse the flags + flag.Parse() + + // Verify that the values are overridden + Expect(cfg.BaseDir).To(Equal("/custom/config/dir")) + Expect(cfg.Address).To(Equal("127.0.0.1:8080")) + Expect(cfg.MetricsPath).To(Equal("/custom/metrics")) + Expect(cfg.EnableGPU).To(BeTrue()) + Expect(cfg.EnableEBPFCgroupID).To(BeFalse()) + Expect(cfg.ExposeHardwareCounterMetrics).To(BeFalse()) + Expect(cfg.EnableMSR).To(BeTrue()) + Expect(cfg.Kubeconfig).To(Equal("/custom/kubeconfig")) + Expect(cfg.ApiserverEnabled).To(BeFalse()) + Expect(cfg.RedfishCredFilePath).To(Equal("/custom/redfish/cred")) + Expect(cfg.ExposeEstimatedIdlePower).To(BeTrue()) + Expect(cfg.MachineSpecFilePath).To(Equal("/custom/machine/spec")) + Expect(cfg.DisablePowerMeter).To(BeTrue()) + Expect(cfg.TLSFilePath).To(Equal("/custom/tls/config")) + }) +}) + +var _ = Describe("HealthProbe", func() { + var ( + w *httptest.ResponseRecorder + req *http.Request + ) + + BeforeEach(func() { + // Initialize the response recorder and request before each test + w = httptest.NewRecorder() + req = httptest.NewRequest("GET", "/health", nil) + }) + + Context("when the health probe is called", func() { + It("should return HTTP status OK", func() { + healthProbe(w, req) + + Expect(w.Code).To(Equal(http.StatusOK)) + }) + + It("should return 'ok' in the response body", func() { + healthProbe(w, req) + + Expect(w.Body.String()).To(Equal("ok")) + }) + }) +}) + +var _ = Describe("RootHandler", func() { + var ( + metricPathConfig string + handler http.HandlerFunc + recorder *httptest.ResponseRecorder + request *http.Request + ) + + BeforeEach(func() { + metricPathConfig = "/metrics" + handler = rootHandler(metricPathConfig) + recorder = httptest.NewRecorder() + request = httptest.NewRequest("GET", "/", nil) + }) + + Context("when the root endpoint is accessed", func() { + It("should return a valid HTML response with the correct metric path", func() { + handler.ServeHTTP(recorder, request) + + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(ContainSubstring("Energy Stats Exporter")) + Expect(recorder.Body.String()).To(ContainSubstring("

Energy Stats Exporter

")) + Expect(recorder.Body.String()).To(ContainSubstring(`Metrics`)) + }) + }) + + Context("when the response writing fails", func() { + It("should log an error", func() { + // Mock the response writer to simulate a write failure + failingRecorder := &FailingResponseRecorder{httptest.NewRecorder(), false} + handler.ServeHTTP(failingRecorder, request) + + // Here you would typically check if the error was logged. + // Since klog is used, you might need to capture logs or mock klog for this test. + // This is a placeholder to indicate where you would check for the error log. + Expect(failingRecorder.Failed).To(BeTrue()) + }) + }) +}) + +// FailingResponseRecorder is a custom ResponseRecorder that fails on Write +type FailingResponseRecorder struct { + *httptest.ResponseRecorder + Failed bool +} + +func (r *FailingResponseRecorder) Write(b []byte) (int, error) { + if strings.Contains(string(b), "Metrics") { + r.Failed = true + return 0, fmt.Errorf("simulated write failure") + } + return r.ResponseRecorder.Write(b) +} diff --git a/pkg/collector/metric_collector_test.go b/pkg/collector/metric_collector_test.go index 00c069ff80..01aeaadf0e 100644 --- a/pkg/collector/metric_collector_test.go +++ b/pkg/collector/metric_collector_test.go @@ -1,6 +1,8 @@ package collector import ( + "testing" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -74,3 +76,23 @@ var _ = Describe("Test Collector Unit", func() { }) }) + +func BenchmarkHandleInactiveContainers(b *testing.B) { + // Initialize the mock exporter and collector + config.Initialize(".") + bpfExporter := bpf.NewMockExporter(bpf.DefaultSupportedMetrics()) + metricCollector := newMockCollector(bpfExporter) + + // Create a map of found containers + foundContainer := make(map[string]bool) + foundContainer["container1"] = true + foundContainer["container2"] = true + + // Reset the timer to exclude setup time from the benchmark + b.ResetTimer() + + // Run the benchmark + for i := 0; i < b.N; i++ { + metricCollector.handleInactiveContainers(foundContainer) + } +}