diff --git a/.github/workflows/step_tests-unit.yml b/.github/workflows/step_tests-unit.yml index 96bf5dc7..7e3eae67 100644 --- a/.github/workflows/step_tests-unit.yml +++ b/.github/workflows/step_tests-unit.yml @@ -30,11 +30,8 @@ jobs: - name: Merge coverage files run: | - # Remove first line from coverage-cgo.out file - tail -n +2 coverage-cgo.out > coverage-cgo.tmp.out && mv coverage-cgo.tmp.out coverage-cgo.out - # Merge coverage-go.out and coverage-cgo.out files - cat coverage-go.out coverage-cgo.out > coverage.out - go tool cover -func=coverage.out -o=coverage.out + # Make global coverage report + make coverage # Remove testdata that contains too many files rm -rf pkg/collector/testdata @@ -58,6 +55,9 @@ jobs: # Create/Update badge gobadge -target README.md -filename coverage.out -link https://github.com/mahendrapaipuri/ceems/actions/workflows/ci.yml?query=branch%3Amain + # Check diff on README + git diff README.md + - name: Verify Changed files uses: tj-actions/verify-changed-files@v19 id: verify-changed-files diff --git a/.gitignore b/.gitignore index 3f1ece8a..88f9de34 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ coverage.txt # Output of the go coverage tool, specifically when used with LiteIDE *.out +*.out.tmp # Dependency directories (remove the comment below to include it) vendor/ diff --git a/Makefile b/Makefile index 95cc4bc3..0bb0dd3c 100644 --- a/Makefile +++ b/Makefile @@ -49,15 +49,19 @@ ifeq ($(CGO_BUILD), 1) checkrules := skip-checkrules # go test flags - test-flags := -coverprofile=coverage-cgo.out + coverage-file := coverage-cgo.out + test-flags := -covermode=atomic -coverprofile=$(coverage-file).tmp else PROMU_CONF ?= .promu-go.yml - pkgs := ./pkg/collector ./pkg/emissions ./pkg/tsdb ./pkg/grafana ./cmd/ceems_exporter + pkgs := ./pkg/collector ./pkg/emissions ./pkg/tsdb ./pkg/grafana \ + ./internal/helpers ./internal/osexec ./internal/structset \ + ./cmd/ceems_exporter checkmetrics := checkmetrics checkrules := checkrules # go test flags - test-flags := -coverprofile=coverage-go.out + coverage-file := coverage-go.out + test-flags := -covermode=atomic -coverprofile=$(coverage-file).tmp endif ifeq ($(GOHOSTOS), linux) @@ -90,10 +94,18 @@ $(eval $(call goarch_pair,mips64el,mipsel)) all:: vet common-all $(cross-test) $(test-docker) $(checkmetrics) $(checkrules) $(test-e2e) +.PHONY: coverage +coverage: + @echo ">> getting coverage report" + tail -n +2 coverage-cgo.out > coverage-cgo.tmp.out && mv coverage-cgo.tmp.out coverage-cgo.out + cat coverage-go.out coverage-cgo.out > coverage.out + $(GO) tool cover -func=coverage.out -o=coverage.out + .PHONY: test test: pkg/collector/testdata/sys/.unpacked pkg/collector/testdata/proc/.unpacked @echo ">> running tests" $(GO) test -short $(test-flags) $(pkgs) + cat $(coverage-file).tmp | grep -v "main.go" > $(coverage-file) .PHONY: test-32bit test-32bit: pkg/collector/testdata/sys/.unpacked diff --git a/internal/structset/structset.go b/internal/structset/structset.go index 51aa61ff..dcc303a2 100644 --- a/internal/structset/structset.go +++ b/internal/structset/structset.go @@ -27,17 +27,17 @@ func GetStructFieldNames(Struct interface{}) []string { return fields } -// GetStructFieldValues returns all values in a given struct -func GetStructFieldValues(Struct interface{}) []interface{} { - v := reflect.ValueOf(Struct) - values := make([]interface{}, v.NumField()) - - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - values = append(values, f.Interface()) - } - return values -} +// // GetStructFieldValues returns all values in a given struct +// func GetStructFieldValues(Struct interface{}) []interface{} { +// v := reflect.ValueOf(Struct) +// values := make([]interface{}, v.NumField()) + +// for i := 0; i < v.NumField(); i++ { +// f := v.Field(i) +// values = append(values, f.Interface()) +// } +// return values +// } // Get tag value of field. If tag value is "-", return lower case value of field name func getTagValue(field reflect.StructField, tag string) string { diff --git a/internal/structset/structset_test.go b/internal/structset/structset_test.go new file mode 100644 index 00000000..87acd4f3 --- /dev/null +++ b/internal/structset/structset_test.go @@ -0,0 +1,45 @@ +package structset + +import ( + "reflect" + "testing" +) + +// testStruct is a test struct that will be used in tests +type testStruct struct { + ID int `json:"id" sql:"id"` + Field1 string `json:"field1" sql:"f1"` + Field2 bool `json:"field2" sql:"f2"` + Field3 interface{} `json:"field3" sql:"f3"` + Field4 []string `json:"field4" sql:"f4"` +} + +func TestGetStructFieldNames(t *testing.T) { + fields := GetStructFieldNames(testStruct{}) + expectedFields := []string{"ID", "Field1", "Field2", "Field3", "Field4"} + if !reflect.DeepEqual(fields, expectedFields) { + t.Errorf("expected %v, got %v", expectedFields, fields) + } +} + +func TestGetStructFieldValues(t *testing.T) { + tags := GetStructFieldTagValues(testStruct{}, "json") + expectedTags := []string{"field1", "field2", "field3", "field4"} + if !reflect.DeepEqual(tags, expectedTags) { + t.Errorf("expected %v, got %v", expectedTags, tags) + } +} + +func TestGetStructFieldTagMap(t *testing.T) { + tagMap := GetStructFieldTagMap(testStruct{}, "json", "sql") + expectedTagMap := map[string]string{ + "id": "id", + "field1": "f1", + "field2": "f2", + "field3": "f3", + "field4": "f4", + } + if !reflect.DeepEqual(tagMap, expectedTagMap) { + t.Errorf("expected %v, got %v", expectedTagMap, tagMap) + } +} diff --git a/pkg/api/db/db_test.go b/pkg/api/db/db_test.go index 0168504f..15620812 100644 --- a/pkg/api/db/db_test.go +++ b/pkg/api/db/db_test.go @@ -310,37 +310,27 @@ func TestJobStatsDBBackup(t *testing.T) { } } -// func TestJobStatsDBBackupFailRetries(t *testing.T) { -// tmpDir := t.TempDir() -// c := prepareMockConfig(tmpDir) - -// // Make new stats DB -// j, err := NewJobStatsDB(c) -// if err != nil { -// t.Errorf("Failed to create new statsDB struct due to %s", err) -// } - -// // Make backup dir non existent -// s.storage.dbBackupPath = "non-existent" - -// // Populate DB with data -// populateDBWithMockData(s.db, j) - -// // Run backup -// for i := 0; i < maxBackupRetries; i++ { -// s.createBackup() -// } -// if s.storage.backupRetries != 0 { -// t.Errorf("Failed to reset DB backup retries counter. Expected 0, got %d", s.storage.backupRetries) -// } - -// for i := 0; i < maxBackupRetries-1; i++ { -// s.createBackup() -// } -// if s.storage.backupRetries != 0 { -// t.Errorf("Failed to increment DB backup retries counter. Expected %d, got %d", maxBackupRetries-1, s.storage.backupRetries) -// } -// } +func TestStatsDBBackup(t *testing.T) { + tmpDir := t.TempDir() + c := prepareMockConfig(tmpDir) + + // Make new stats DB + s, err := NewStatsDB(c) + if err != nil { + t.Errorf("Failed to create new statsDB struct due to %s", err) + } + + // Make backup dir non existent + s.storage.dbBackupPath = tmpDir + + // Populate DB with data + populateDBWithMockData(s) + + // Run backup + if err := s.createBackup(); err != nil { + t.Errorf("Failed to backup DB: %s", err) + } +} func TestJobStatsDeleteOldUnits(t *testing.T) { tmpDir := t.TempDir() diff --git a/pkg/api/helper/helper_test.go b/pkg/api/helper/helper_test.go index 773e04e7..b375ce8c 100644 --- a/pkg/api/helper/helper_test.go +++ b/pkg/api/helper/helper_test.go @@ -1,8 +1,11 @@ package helper import ( + "fmt" "reflect" "testing" + + "github.com/mahendrapaipuri/ceems/pkg/api/base" ) type nodelistParserTest struct { @@ -128,3 +131,27 @@ func TestNodelistParser(t *testing.T) { } } } + +func TestTimeToTimestamp(t *testing.T) { + expectedTimeStamp := 1136239445000 + timeFormat := fmt.Sprintf("%s-0700", base.DatetimeLayout) + timeStamp := TimeToTimestamp(timeFormat, "2006-01-02T15:04:05-0700") + if timeStamp != int64(expectedTimeStamp) { + t.Errorf("expected timestamp %d, got %d", expectedTimeStamp, timeStamp) + } + + // Check failure case + timeStamp = TimeToTimestamp(timeFormat, "2006-01-0215:04:05-0700") + if timeStamp != 0 { + t.Errorf("expected timestamp 0, got %d", timeStamp) + } +} + +func TestChunkBy(t *testing.T) { + expectedChunks := [][]int{{1, 2, 3}, {4, 5, 6}} + inputSlice := []int{1, 2, 3, 4, 5, 6} + chunks := ChunkBy(inputSlice, 3) + if !reflect.DeepEqual(expectedChunks, chunks) { + t.Errorf("expected chunks %v, got %v", expectedChunks, chunks) + } +} diff --git a/pkg/api/http/error_test.go b/pkg/api/http/error_test.go new file mode 100644 index 00000000..bc0384b3 --- /dev/null +++ b/pkg/api/http/error_test.go @@ -0,0 +1,13 @@ +package http + +import ( + "fmt" + "testing" +) + +func TestApiError(t *testing.T) { + e := apiError{typ: errorBadData, err: fmt.Errorf("bad data")} + if e.Error() != "bad_data: bad data" { + t.Errorf("expected error bad_data: bad data, got %s", e.Error()) + } +}