Skip to content

Commit

Permalink
ci: add codecov support (#6)
Browse files Browse the repository at this point in the history
* test: add unit tests for pkg/utils
* ci: add codecov to build.yml

Signed-off-by: Simon Leet <simon.leet@microsoft.com>
  • Loading branch information
CodeMonkeyLeet committed Jan 17, 2023
1 parent fb241b0 commit 51184a0
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 2 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ jobs:
with:
imageName: ${{ env.DEVCON_NAME }}
imageTag: ${{ env.DEVCON_VERSION }}
env: |
CODECOV_OPTS=-coverprofile=coverage.txt -covermode=atomic
runCmd: |
set -e
make build
Expand All @@ -54,6 +56,8 @@ jobs:
with:
name: copa_edge_linux_amd64.tar.gz
path: dist/linux_amd64/release/copa_edge_linux_amd64.tar.gz
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
- name: Load test cases for patch testing
id: load-tests
run: |
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ release: build archive
.PHONY: test
test:
$(info $(INFOMARK) Running unit tests on pkg libraries ...)
go test ./pkg/...
go test ./pkg/... $(CODECOV_OPTS)

################################################################################
# Target: clean #
Expand Down
11 changes: 11 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
coverage:
# Commit status https://docs.codecov.io/docs/commit-status are used
# to block PR based on coverage threshold.
status:
project:
default:
target: auto
threshold: 0%
patch:
default:
informational: true
53 changes: 53 additions & 0 deletions pkg/utils/log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// ------------------------------------------------------------
// Copyright (c) Project Copacetic authors.
// Licensed under the MIT License.
// ------------------------------------------------------------

package utils

import (
"bytes"
"fmt"
"os/exec"
"strings"
"testing"
"time"

log "github.com/sirupsen/logrus"
)

const testLogPipeMsg = "Test LogPipe message"

func TestLogPipe(t *testing.T) {
cmd := exec.Command("echo", testLogPipeMsg)
stdout, err := cmd.StdoutPipe()
if err != nil {
t.Fatalf("Failed to get stdout pipe: %s", err)
return
}

stdOutBuf := new(bytes.Buffer)
log.StandardLogger().SetOutput(stdOutBuf)
go LogPipe(stdout, log.InfoLevel)
err = cmd.Run()
if err != nil {
t.Fatalf("Failed to run command: %s", err)
return
}

expected := fmt.Sprintf("level=info msg=\"%s\"", testLogPipeMsg)
start := time.Now()
for stdOutBuf.Len() < len(expected) {
if time.Since(start) > 10*time.Millisecond {
t.Errorf("LogPipe() did not finish write within 10ms")
return
}
// Wait for LogPipe to finish writing, should be on the order of ns
time.Sleep(1 * time.Millisecond)
}

if !strings.Contains(stdOutBuf.String(), fmt.Sprintf("level=info msg=\"%s\"", testLogPipeMsg)) {
t.Errorf("LogPipe() result: \"%s\", want: \"%s\"", stdOutBuf.String(), testLogPipeMsg)
return
}
}
9 changes: 8 additions & 1 deletion pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ import (

func EnsurePath(path string, perm fs.FileMode) (bool, error) {
createdPath := false
_, err := os.Stat(path)
st, err := os.Stat(path)
if err != nil && os.IsNotExist(err) {
err = os.MkdirAll(path, perm)
createdPath = (err == nil)
} else {
if !st.IsDir() {
return false, fs.ErrExist
}
if st.Mode().Perm() != perm {
return false, fs.ErrPermission
}
}
return createdPath, err
}
Expand Down
142 changes: 142 additions & 0 deletions pkg/utils/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// ------------------------------------------------------------
// Copyright (c) Project Copacetic authors.
// Licensed under the MIT License.
// ------------------------------------------------------------

package utils

import (
"log"
"os"
"path"
"testing"
)

const (
newDir = "a/b/new_path"
diffPermsDir = "a/diff_perms"
existingDir = "a/dir_exists"
emptyFile = "a/empty_file"
nonemptyFile = "a/nonempty_file"

// Note that we are using the /tmp folder, so use perms that
// do not conflict with the sticky bit.
testPerms = 0o711
)

// Global for the test root directory used by all tests.
var testRootDir string

func TestMain(m *testing.M) {
// Create the root temp test directory.
var err error
testRootDir, err = os.MkdirTemp("", "utils_test_*")
if err != nil {
log.Println("Failed to create test temp folder")
return
}
defer os.RemoveAll(testRootDir)

// Create a test directory with different permissions.
testDir := path.Join(testRootDir, diffPermsDir)
err = os.MkdirAll(testDir, 0o744)
if err != nil {
log.Printf("Failed to create test folder: %s\n", err)
return
}

// Create an existing test directory.
testDir = path.Join(testRootDir, existingDir)
err = os.MkdirAll(testDir, testPerms)
if err != nil {
log.Printf("Failed to create test folder %s\n", testDir)
return
}

// Create an empty test file.
testFile := path.Join(testRootDir, emptyFile)
f, err := os.Create(testFile)
if err != nil {
log.Printf("Failed to create test file %s\n", testFile)
return
}
f.Close()

// Create a non-empty test file.
testFile = path.Join(testRootDir, nonemptyFile)
f, err = os.Create(testFile)
if err != nil {
log.Printf("Failed to create test file %s\n", testFile)
return
}
_, err = f.WriteString("This is a non-empty test file")
f.Close()
if err != nil {
log.Printf("Failed to write to test file: %s\n", err)
return
}

m.Run()
}

func TestEnsurePath(t *testing.T) {
type args struct {
path string
perm os.FileMode
}
tests := []struct {
name string
args args
created bool
wantErr bool
}{
{"CreateNewPath", args{newDir, testPerms}, true, false},
{"PathExists", args{existingDir, testPerms}, false, false},
{"PathExistsWithDiffPerms", args{diffPermsDir, testPerms}, false, true},
{"PathIsFile", args{emptyFile, testPerms}, false, true},
{"EmptyPath", args{"", testPerms}, false, true},
{"EmptyPerms", args{existingDir, 0o000}, false, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testPath := path.Join(testRootDir, tt.args.path)
createdPath, err := EnsurePath(testPath, tt.args.perm)
if (err != nil) != tt.wantErr {
t.Errorf("EnsurePath() error = %v, wantErr %v", err, tt.wantErr)
return
}
if createdPath != tt.created {
t.Errorf("EnsurePath() created = %v, want %v", createdPath, tt.created)
}
})
}
// Clean up new path in case go test is run for -count > 1
os.Remove(path.Join(testRootDir, newDir))
}

func TestIsNonEmptyFile(t *testing.T) {
type args struct {
dir string
file string
}
tests := []struct {
name string
args args
want bool
}{
{"NonEmptyFile", args{testRootDir, nonemptyFile}, true},
{"EmptyFile", args{testRootDir, emptyFile}, false},
{"MissingFile", args{testRootDir, "does_not_exist"}, false},
{"UnspecifiedPath", args{"", existingDir}, false},
{"UnspecifiedFile", args{testRootDir, ""}, false},
{"PathIsDirectory", args{testRootDir, existingDir}, false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsNonEmptyFile(tt.args.dir, tt.args.file); got != tt.want {
t.Errorf("IsNonEmptyFile() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 51184a0

Please sign in to comment.