Skip to content
This repository has been archived by the owner on Feb 17, 2024. It is now read-only.

Commit

Permalink
Merge pull request #37 from stealthrocket/integration-tests
Browse files Browse the repository at this point in the history
timecraft: add integration test suite
  • Loading branch information
achille-roussel committed May 31, 2023
2 parents 00f0ae1 + da8759f commit 3fa01f8
Show file tree
Hide file tree
Showing 28 changed files with 638 additions and 191 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ timecraft.src.go = \
$(wildcard */*/*.go) \
$(wildcard */*/*/*.go)

timecraft: go.mod $(timecraft.src.go)
timecraft: go.mod flatbuffers $(timecraft.src.go)
$(GO) build -o timecraft

clean:
Expand All @@ -32,7 +32,7 @@ generate: flatbuffers
flatbuffers: go.mod $(format.src.go)
$(GO) build ./format/...

test: flatbuffers testdata
test: timecraft testdata
$(GO) test ./...

testdata: $(testdata.go.wasm)
Expand Down
4 changes: 2 additions & 2 deletions internal/cmd/config.go → config.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cmd
package main

import (
"bytes"
Expand All @@ -24,7 +24,7 @@ Options:
-c, --config Path to the timecraft configuration file (overrides TIMECRAFTCONFIG)
--edit Open $EDITOR to edit the configuration
-h, --help Show usage information
-o, --ouptut format Output format, one of: text, json, yaml
-o, --output format Output format, one of: text, json, yaml
`

var (
Expand Down
4 changes: 2 additions & 2 deletions internal/cmd/describe.go → describe.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cmd
package main

import (
"context"
Expand Down Expand Up @@ -49,7 +49,7 @@ Examples:
Options:
-c, --config Path to the timecraft configuration file (overrides TIMECRAFTCONFIG)
-h, --help Show this usage information
-o, --ouptut format Output format, one of: text, json, yaml
-o, --output format Output format, one of: text, json, yaml
`

func describe(ctx context.Context, args []string) error {
Expand Down
9 changes: 4 additions & 5 deletions internal/cmd/export.go → export.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cmd
package main

import (
"context"
Expand All @@ -11,7 +11,7 @@ import (
)

const exportUsage = `
Usage: timecraft export <resource type> <resource id> <output file>
Usage: timecraft export <resource type> <resource id> <output file> [options]
The export command reads resources from the time machine registry and writes
them to local files. This command is useful to extract data generated by
Expand All @@ -31,12 +31,11 @@ func export(ctx context.Context, args []string) error {
args = parseFlags(flagSet, args)

if len(args) != 3 {
return errors.New(`expected resource type, id, and output file as argument` + useCmd("export"))
return usageError(`Expected resource type, id, and output file as argument` + useCmd("export"))
}

resource, err := findResource("describe", args[0])
if err != nil {
return err
return usageError(err.Error())
}
config, err := loadConfig()
if err != nil {
Expand Down
66 changes: 66 additions & 0 deletions export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main_test

import (
"os"
"strings"
"testing"

"github.com/stealthrocket/timecraft/internal/assert"
)

var export = tests{
"show the export command help with the short option": func(t *testing.T) {
stdout, stderr, err := timecraft(t, "export", "-h")
assert.OK(t, err)
assert.HasPrefix(t, stdout, "Usage:\ttimecraft export ")
assert.Equal(t, stderr, "")
},

"show the export command help with the long option": func(t *testing.T) {
stdout, stderr, err := timecraft(t, "export", "--help")
assert.OK(t, err)
assert.HasPrefix(t, stdout, "Usage:\ttimecraft export ")
assert.Equal(t, stderr, "")
},

"export without a resource type": func(t *testing.T) {
stdout, stderr, err := timecraft(t, "export")
assert.ExitError(t, err, 2)
assert.Equal(t, stdout, "")
assert.HasPrefix(t, stderr, "Expected resource type, id, and output file as argument")
},

"export without a resource id": func(t *testing.T) {
stdout, stderr, err := timecraft(t, "export", "profile")
assert.ExitError(t, err, 2)
assert.Equal(t, stdout, "")
assert.HasPrefix(t, stderr, "Expected resource type, id, and output file as argument")
},

"export without an output file": func(t *testing.T) {
stdout, stderr, err := timecraft(t, "export", "profile", "74080192e42e")
assert.ExitError(t, err, 2)
assert.Equal(t, stdout, "")
assert.HasPrefix(t, stderr, "Expected resource type, id, and output file as argument")
},

"export a module to stdout": func(t *testing.T) {
stdout, processID, err := timecraft(t, "run", "./testdata/go/sleep.wasm", "1ns")
assert.OK(t, err)
assert.Equal(t, stdout, "sleeping for 1ns\n")
assert.NotEqual(t, processID, "")

moduleID, stderr, err := timecraft(t, "get", "mod", "-q")
assert.OK(t, err)
assert.Equal(t, stderr, "")
moduleID = strings.TrimSuffix(moduleID, "\n")

moduleData, stderr, err := timecraft(t, "export", "mod", moduleID, "-")
assert.OK(t, err)
assert.Equal(t, stderr, "")

sleepWasm, err := os.ReadFile("./testdata/go/sleep.wasm")
assert.OK(t, err)
assert.True(t, moduleData == string(sleepWasm))
},
}
55 changes: 33 additions & 22 deletions internal/cmd/get.go → get.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package cmd
package main

import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -47,14 +46,15 @@ Examples:
Options:
-c, --config Path to the timecraft configuration file (overrides TIMECRAFTCONFIG)
-h, --help Show this usage information
-o, --ouptut format Output format, one of: text, json, yaml
-o, --output format Output format, one of: text, json, yaml
-q, --quiet Only display the resource ids
`

type resource struct {
typ string
alt []string
mediaType format.MediaType
get func(context.Context, io.Writer, *timemachine.Registry) stream.WriteCloser[*format.Descriptor]
get func(context.Context, io.Writer, *timemachine.Registry, bool) stream.WriteCloser[*format.Descriptor]
describe func(context.Context, *timemachine.Registry, string) (any, error)
lookup func(context.Context, *timemachine.Registry, string) (any, error)
}
Expand Down Expand Up @@ -118,18 +118,20 @@ func get(ctx context.Context, args []string) error {
var (
timeRange = timemachine.Since(time.Unix(0, 0))
output = outputFormat("text")
quiet = false
)

flagSet := newFlagSet("timecraft get", getUsage)
customVar(flagSet, &output, "o", "output")
boolVar(flagSet, &quiet, "q", "quiet")
args = parseFlags(flagSet, args)

if len(args) != 1 {
return errors.New(`expected exactly one resource type as argument` + useCmd("get"))
return usageError(`Expected exactly one resource type as argument` + useCmd("get"))
}
resource, err := findResource("get", args[0])
if err != nil {
return err
return usageError(err.Error())
}
config, err := loadConfig()
if err != nil {
Expand All @@ -153,7 +155,7 @@ func get(ctx context.Context, args []string) error {
case "yaml":
writer = yamlprint.NewWriter[*format.Manifest](os.Stdout)
default:
writer = getLogs(ctx, os.Stdout, registry)
writer = getLogs(ctx, os.Stdout, registry, quiet)
}
defer writer.Close()

Expand All @@ -171,22 +173,22 @@ func get(ctx context.Context, args []string) error {
case "yaml":
writer = yamlprint.NewWriter[*format.Descriptor](os.Stdout)
default:
writer = resource.get(ctx, os.Stdout, registry)
writer = resource.get(ctx, os.Stdout, registry, quiet)
}
defer writer.Close()

_, err = stream.Copy[*format.Descriptor](writer, reader)
return err
}

func getConfigs(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getConfigs(ctx context.Context, w io.Writer, reg *timemachine.Registry, quiet bool) stream.WriteCloser[*format.Descriptor] {
type config struct {
ID string `text:"CONFIG ID"`
Runtime string `text:"RUNTIME"`
Modules int `text:"MODULES"`
Size human.Bytes `text:"SIZE"`
}
return newTableWriter(w,
return newTableWriter(w, quiet,
func(c1, c2 config) bool {
return c1.ID < c2.ID
},
Expand All @@ -208,13 +210,13 @@ func getConfigs(ctx context.Context, w io.Writer, reg *timemachine.Registry) str
})
}

func getModules(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getModules(ctx context.Context, w io.Writer, reg *timemachine.Registry, quiet bool) stream.WriteCloser[*format.Descriptor] {
type module struct {
ID string `text:"MODULE ID"`
Name string `text:"MODULE NAME"`
Size human.Bytes `text:"SIZE"`
}
return newTableWriter(w,
return newTableWriter(w, quiet,
func(m1, m2 module) bool {
return m1.ID < m2.ID
},
Expand All @@ -231,12 +233,12 @@ func getModules(ctx context.Context, w io.Writer, reg *timemachine.Registry) str
})
}

func getProcesses(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getProcesses(ctx context.Context, w io.Writer, reg *timemachine.Registry, quiet bool) stream.WriteCloser[*format.Descriptor] {
type process struct {
ID format.UUID `text:"PROCESS ID"`
StartTime human.Time `text:"START"`
}
return newTableWriter(w,
return newTableWriter(w, quiet,
func(p1, p2 process) bool {
return time.Time(p1.StartTime).Before(time.Time(p2.StartTime))
},
Expand All @@ -252,7 +254,7 @@ func getProcesses(ctx context.Context, w io.Writer, reg *timemachine.Registry) s
})
}

func getProfiles(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getProfiles(ctx context.Context, w io.Writer, reg *timemachine.Registry, quiet bool) stream.WriteCloser[*format.Descriptor] {
type profile struct {
ID string `text:"PROFILE ID"`
ProcessID format.UUID `text:"PROCESS ID"`
Expand All @@ -261,7 +263,7 @@ func getProfiles(ctx context.Context, w io.Writer, reg *timemachine.Registry) st
Duration human.Duration `text:"DURATION"`
Size human.Bytes `text:"SIZE"`
}
return newTableWriter(w,
return newTableWriter(w, quiet,
func(p1, p2 profile) bool {
if p1.ProcessID != p2.ProcessID {
return bytes.Compare(p1.ProcessID[:], p2.ProcessID[:]) < 0
Expand Down Expand Up @@ -289,13 +291,13 @@ func getProfiles(ctx context.Context, w io.Writer, reg *timemachine.Registry) st
})
}

func getRuntimes(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Descriptor] {
func getRuntimes(ctx context.Context, w io.Writer, reg *timemachine.Registry, quiet bool) stream.WriteCloser[*format.Descriptor] {
type runtime struct {
ID string `text:"RUNTIME ID"`
Runtime string `text:"RUNTIME NAME"`
Version string `text:"VERSION"`
}
return newTableWriter(w,
return newTableWriter(w, quiet,
func(r1, r2 runtime) bool {
return r1.ID < r2.ID
},
Expand All @@ -312,14 +314,14 @@ func getRuntimes(ctx context.Context, w io.Writer, reg *timemachine.Registry) st
})
}

func getLogs(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream.WriteCloser[*format.Manifest] {
func getLogs(ctx context.Context, w io.Writer, reg *timemachine.Registry, quiet bool) stream.WriteCloser[*format.Manifest] {
type manifest struct {
ProcessID format.UUID `text:"PROCESS ID"`
Segments human.Count `text:"SEGMENTS"`
StartTime human.Time `text:"START"`
Size human.Bytes `text:"SIZE"`
}
return newTableWriter(w,
return newTableWriter(w, quiet,
func(m1, m2 manifest) bool {
return time.Time(m1.StartTime).Before(time.Time(m2.StartTime))
},
Expand All @@ -336,8 +338,17 @@ func getLogs(ctx context.Context, w io.Writer, reg *timemachine.Registry) stream
})
}

func newTableWriter[T1, T2 any](w io.Writer, orderBy func(T1, T1) bool, conv func(T2) (T1, error)) stream.WriteCloser[T2] {
tw := textprint.NewTableWriter[T1](w, textprint.OrderBy(orderBy))
func newTableWriter[T1, T2 any](w io.Writer, quiet bool, orderBy func(T1, T1) bool, conv func(T2) (T1, error)) stream.WriteCloser[T2] {
opts := []textprint.TableOption[T1]{
textprint.OrderBy(orderBy),
}
if quiet {
opts = append(opts,
textprint.Header[T1](false),
textprint.List[T1](true),
)
}
tw := textprint.NewTableWriter[T1](w, opts...)
cw := stream.ConvertWriter[T1](tw, conv)
return stream.NewWriteCloser(cw, tw)
}
Expand Down
Loading

0 comments on commit 3fa01f8

Please sign in to comment.