Skip to content

Commit

Permalink
improve some func
Browse files Browse the repository at this point in the history
- fix race condition(weak)
- add logging
  • Loading branch information
robotomize committed Apr 19, 2023
1 parent bb99deb commit 108eb3c
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 84 deletions.
66 changes: 38 additions & 28 deletions cmd/golurectl/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"strings"

"github.com/robotomize/go-allure/internal/fs"
"github.com/spf13/cobra"

"github.com/robotomize/go-allure/internal/allure"
Expand Down Expand Up @@ -99,14 +100,14 @@ func init() {
"attachment-force",
"a",
false,
"add a log of pass and failed tests to the attachments",
"create attachments for passed tests",
)
rootCmd.PersistentFlags().BoolVarP(
&silentOutput,
"silent",
"s",
false,
"silent report output",
"silent allure report output(JSON)",
)
}

Expand Down Expand Up @@ -135,44 +136,29 @@ var rootCmd = &cobra.Command{
buildArgs = append([]string{"-tags"}, strings.Split(strings.TrimSpace(goBuildTagsFlag), ",")...)
}

r := gotest.NewReader(os.Stdin)
p := parser.New(golist.NewRetriever(os.DirFS(pwd), buildArgs...))

e := exporter.New(p, r, opts...)
if err := e.Read(ctx); err != nil {
pkgReader := gotest.NewReader(os.Stdin)
goParser := parser.New(golist.NewRetriever(fs.New(pwd), buildArgs...))
allureExporter := exporter.New(goParser, pkgReader, opts...)
if err := allureExporter.Read(ctx); err != nil {
return fmt.Errorf("exporter Read: %w", err)
}

result, err := e.Export()
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nTrying to generate an allure report\n")
allureReport, err := allureExporter.Export()
if err != nil {
return fmt.Errorf("exporter Export: %w", err)
}

if verboseFlag && result.Err != nil {
if _, err := cmd.OutOrStdout().Write([]byte(result.Err.Error())); err != nil {
return err
}
if verboseFlag && allureReport.Err != nil {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Read go test output log: %s", allureReport.Err.Error())
}

if forwardGoTestLog {
if _, err := io.Copy(cmd.OutOrStdout(), result.OutputLog); err != nil {
if _, err := io.Copy(cmd.OutOrStdout(), allureReport.OutputLog); err != nil {
return fmt.Errorf("io.сopy: %w", err)
}
}

if forwardGoTestExitCode {
var failed bool
for _, tc := range result.Tests {
if !failed && (tc.Status == allure.StatusFail || tc.Status == allure.StatusBroken) {
failed = true
}
}

if failed {
os.Exit(1)
}
}

var outOpts []exporter.WriterOption
if outputDirFlag != "" {
outOpts = append(outOpts, exporter.WithOutputPth(outputDirFlag))
Expand All @@ -185,14 +171,38 @@ var rootCmd = &cobra.Command{

writer := exporter.NewWriter(outputWriter, outOpts...)

if err := writer.WriteReport(ctx, result.Tests); err != nil {
if len(outputDirFlag) > 0 && len(allureReport.Tests) > 0 {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Write report files\n")
}

if err := writer.WriteReport(ctx, allureReport.Tests); err != nil {
return fmt.Errorf("exporter.NewWriter WriteReport: %w", err)
}

if err := writer.WriteAttachments(ctx, result.Attachments); err != nil {
if len(outputDirFlag) > 0 && len(allureReport.Attachments) > 0 {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Write attachments\n")
}

if err := writer.WriteAttachments(ctx, allureReport.Attachments); err != nil {
return fmt.Errorf("exporter.NewWriter WriteAttachments: %w", err)
}

_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Conversion completed successfully\n")

if forwardGoTestExitCode {
var failed bool
for _, tc := range allureReport.Tests {
if !failed && (tc.Status == allure.StatusFail || tc.Status == allure.StatusBroken) {
failed = true
}
}

if failed {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "One or more go tests failed. exiting with error 1\n")
os.Exit(1)
}
}

return nil
},
}
Expand Down
22 changes: 11 additions & 11 deletions internal/exporter/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,26 +119,26 @@ func (e *exporter) Read(ctx context.Context) error {
}

func (e *exporter) Export() (Report, error) {
var result Report
const goOutputSize = 4096

const sampleBufferSize = 4096
goOutputBuf := bytes.NewBuffer(make([]byte, 0, goOutputSize))

logBuf := bytes.NewBuffer(make([]byte, 0, sampleBufferSize))
result.OutputLog = logBuf
result := Report{
Err: e.readErr,
OutputLog: goOutputBuf,
}

result.OutputLog = goOutputBuf

attachmentCh := make(chan Attachment)
wg := &sync.WaitGroup{}
wg.Add(1)

attachments := make([]Attachment, 0)
defer func() {
result.Attachments = append(result.Attachments, attachments...)
}()

go func() {
defer wg.Done()

for attachment := range attachmentCh {
attachments = append(attachments, attachment)
result.Attachments = append(result.Attachments, attachment)
}
}()

Expand Down Expand Up @@ -205,7 +205,7 @@ func (e *exporter) Export() (Report, error) {
e.addStep(&allureTestCase, testCase, attachmentCh)
result.Tests = append(result.Tests, allureTestCase)

logBuf.Write(testCase.Log)
goOutputBuf.Write(testCase.Log)
}

close(attachmentCh)
Expand Down
26 changes: 26 additions & 0 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package fs

import (
"io/fs"
"os"
)

type FS interface {
fs.FS
RootDir() string
}

var _ FS = (*rootDirFS)(nil)

func New(entry string) FS {
return &rootDirFS{entry: entry, FS: os.DirFS(entry)}
}

type rootDirFS struct {
fs.FS
entry string
}

func (r rootDirFS) RootDir() string {
return r.entry
}
89 changes: 46 additions & 43 deletions internal/golist/golist.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"io/fs"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -40,18 +41,22 @@ type Module struct {
GoVersion string `json:"GoVersion"`
}

func DirPackages(ctx context.Context, sfs fs.FS, args ...string) ([]Package, error) {
func DirPackages(ctx context.Context, dfs FS, args ...string) ([]Package, error) {
packages := make([]Package, 0)

if err := fs.WalkDir(
sfs, ".", func(pth string, entry fs.DirEntry, err error) error {
dfs, ".", func(pth string, entry fs.DirEntry, err error) error {
skip := strings.HasPrefix(entry.Name(), ".") || strings.HasPrefix(entry.Name(), "..") || entry.IsDir()
if skip {
return nil
}

if entry.Name() == "go.mod" {
d, _ := filepath.Split(pth)
if d == "" {
d = dfs.RootDir()
}

list, err := listPackages(ctx, d, args...)
if err != nil {
return fmt.Errorf("listPackages: %w", err)
Expand All @@ -70,16 +75,24 @@ func DirPackages(ctx context.Context, sfs fs.FS, args ...string) ([]Package, err
}

func listPackages(ctx context.Context, dir string, args ...string) ([]Package, error) {
names, err := packageNames(dir)
var pkgNames []string
packagesBuf, err := goList(ctx, dir, append(args, "./..."))
if err != nil {
return nil, fmt.Errorf("packageNames: %w", err)
return nil, err
}

goListArgs := append([]string{"list", "-json"}, args...)
goPackages := make([]Package, 0, len(names))
scanner := bufio.NewScanner(packagesBuf)
for scanner.Scan() {
if err = scanner.Err(); err != nil {
return nil, fmt.Errorf("bufio.NewScaner.Err: %w", err)
}

ch := make(chan Package)
pkgNames = append(pkgNames, scanner.Text())
}

goPkgs := make([]Package, 0, len(pkgNames))

ch := make(chan Package)
closeCh := make(chan struct{})

wg, grpCtx := errgroup.WithContext(ctx)
Expand All @@ -88,14 +101,14 @@ func listPackages(ctx context.Context, dir string, args ...string) ([]Package, e
go func() {
defer close(closeCh)

for goPackage := range ch {
goPackages = append(goPackages, goPackage)
for pkg := range ch {
goPkgs = append(goPkgs, pkg)
}
}()

OuterLoop:
for _, packageName := range names {
packageName := packageName
for _, pkgName := range pkgNames {
pkg := pkgName

select {
case <-grpCtx.Done():
Expand All @@ -105,28 +118,20 @@ OuterLoop:

wg.Go(
func() error {
select {
case <-grpCtx.Done():
return nil
default:
}
pkgArgs := append([]string{"-json"}, args...)
pkgArgs = append(pkgArgs, pkg)

const sampleBufferSize = 4096
buf := bytes.NewBuffer(make([]byte, 0, sampleBufferSize))
goListCmd := exec.Command("go", append(goListArgs, packageName)...)
goListCmd.Stdout = buf
goListCmd.Dir = dir
goListCmd.Stdin = strings.NewReader("")
if err = goListCmd.Run(); err != nil {
return fmt.Errorf("command Run go list -json %s.: %w", packageName, err)
packageBuf, pkgErr := goList(grpCtx, dir, pkgArgs)
if pkgErr != nil {
return fmt.Errorf("goList: %w", pkgErr)
}

var goPackage Package
if err = json.Unmarshal(buf.Bytes(), &goPackage); err != nil {
return fmt.Errorf("json.Unmarshal: %w", err)
var goPkg Package
if dErr := json.NewDecoder(packageBuf).Decode(&goPkg); dErr != nil {
return fmt.Errorf("json.NewDecoder.Decode: %w", dErr)
}

ch <- goPackage
ch <- goPkg

return nil
},
Expand All @@ -145,25 +150,23 @@ OuterLoop:
case <-closeCh:
}

return goPackages, nil
return goPkgs, nil
}

func packageNames(dir string) ([]string, error) {
var names []string
func goList(ctx context.Context, dir string, args []string) (io.Reader, error) {
const bufSize = 4096

buf := bytes.NewBuffer(make([]byte, 0, 4096))
goListCmd := exec.Command("go", "list", "./...")
goListCmd.Stdin = strings.NewReader("")
goListCmd.Stdout = buf
goListCmd.Dir = dir
if err := goListCmd.Run(); err != nil {
return nil, fmt.Errorf("command Run go list %s/./...: %w", dir, err)
}
b := bytes.NewBuffer(make([]byte, 0, bufSize))
cmd := exec.CommandContext(ctx, "go", append([]string{"list"}, args...)...)
cmd.Stdout = b
cmd.Dir = dir

scanner := bufio.NewScanner(buf)
for scanner.Scan() {
names = append(names, scanner.Text())
cmd.Stdin = strings.NewReader("")
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("command Run go %s: %w", strings.Join(args, " "), err)
}

return names, nil
b1 := bytes.NewBuffer(b.Bytes())

return b1, nil
}
9 changes: 7 additions & 2 deletions internal/golist/retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ import (
"io/fs"
)

type FS interface {
fs.FS
RootDir() string
}

type PackageRetriever interface {
Retrieve(ctx context.Context) ([]Package, error)
}

func NewRetriever(fs fs.FS, goBuildTags ...string) PackageRetriever {
func NewRetriever(fs FS, goBuildTags ...string) PackageRetriever {
return &retriever{fs: fs, args: goBuildTags}
}

type retriever struct {
fs fs.FS
fs FS
args []string
}

Expand Down

0 comments on commit 108eb3c

Please sign in to comment.