Skip to content

Commit

Permalink
add testing for bubbletea component event handlers
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
  • Loading branch information
wagoodman committed Jun 22, 2023
1 parent 5d160f7 commit a15a9db
Show file tree
Hide file tree
Showing 49 changed files with 1,735 additions and 137 deletions.
1 change: 1 addition & 0 deletions cmd/syft/cli/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions, po
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
checkForApplicationUpdate()
// TODO: this is broke, the bus isn't available yet
}

return attest.Run(cmd.Context(), app, args)
Expand Down
1 change: 1 addition & 0 deletions cmd/syft/cli/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func Convert(v *viper.Viper, app *config.Application, ro *options.RootOptions, p
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
checkForApplicationUpdate()
// TODO: this is broke, the bus isn't available yet
}
return convert.Run(cmd.Context(), app, args)
},
Expand Down
8 changes: 4 additions & 4 deletions cmd/syft/cli/eventloop/event_loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/wagoodman/go-partybus"

"github.com/anchore/clio"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
)

// EventLoop listens to worker errors (from execution path), worker events (from a partybus subscription), and
// signal interrupts. Is responsible for handling each event relative to a given UI an to coordinate eventing until
// an eventual graceful exit.
func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, cleanupFn func(), uxs ...ui.UI) error {
func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, cleanupFn func(), uxs ...clio.UI) error {
defer cleanupFn()
events := subscription.Events()
var err error
var ux ui.UI
var ux clio.UI

if ux, err = setupUI(subscription, uxs...); err != nil {
return err
Expand Down Expand Up @@ -85,7 +85,7 @@ func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *
// during teardown. With the given UIs, the first UI which the ui.Setup() function does not return an error
// will be utilized in execution. Providing a set of UIs allows for the caller to provide graceful fallbacks
// when there are environmental problem (e.g. unable to setup a TUI with the current TTY).
func setupUI(subscription *partybus.Subscription, uis ...ui.UI) (ui.UI, error) {
func setupUI(subscription *partybus.Subscription, uis ...clio.UI) (clio.UI, error) {
for _, ux := range uis {
if err := ux.Setup(subscription); err != nil {
log.Warnf("unable to setup given UI, falling back to alternative UI: %+v", err)
Expand Down
21 changes: 12 additions & 9 deletions cmd/syft/cli/eventloop/event_loop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,37 @@ import (
"github.com/stretchr/testify/mock"
"github.com/wagoodman/go-partybus"

"github.com/anchore/syft/internal/ui"
"github.com/anchore/clio"
"github.com/anchore/syft/syft/event"
)

var _ ui.UI = (*uiMock)(nil)
var _ clio.UI = (*uiMock)(nil)

type uiMock struct {
t *testing.T
finalEvent partybus.Event
unsubscribe func() error
t *testing.T
finalEvent partybus.Event
subscription partybus.Unsubscribable
mock.Mock
}

func (u *uiMock) Setup(unsubscribe func() error) error {
func (u *uiMock) Setup(unsubscribe partybus.Unsubscribable) error {
u.t.Helper()
u.t.Logf("UI Setup called")
u.unsubscribe = unsubscribe
return u.Called(unsubscribe).Error(0)
u.subscription = unsubscribe
return u.Called(unsubscribe.Unsubscribe).Error(0)
}

func (u *uiMock) Handle(event partybus.Event) error {
u.t.Helper()
u.t.Logf("UI Handle called: %+v", event.Type)
if event == u.finalEvent {
assert.NoError(u.t, u.unsubscribe())
assert.NoError(u.t, u.subscription.Unsubscribe())
}
return u.Called(event).Error(0)
}

func (u *uiMock) Teardown(_ bool) error {
u.t.Helper()
u.t.Logf("UI Teardown called")
return u.Called().Error(0)
}
Expand Down
1 change: 0 additions & 1 deletion cmd/syft/cli/options/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ func MakeWriter(outputs []string, defaultFile, templateFilePath string) (sbom.Wr

// MakeWriterForFormat creates a sbom.Writer for for the given format or returns an error.
func MakeWriterForFormat(format sbom.Format, path string) (sbom.Writer, error) {

w := sbomPipeWriter()

writer, err := sbom.NewWriter(w, sbom.NewWriterOption(format, path))
Expand Down
1 change: 1 addition & 0 deletions cmd/syft/cli/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func Packages(v *viper.Viper, app *config.Application, ro *options.RootOptions,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
// TODO: this is broke, the bus isn't available yet
checkForApplicationUpdate()
}
return packages.Run(cmd.Context(), app, args)
Expand Down
1 change: 0 additions & 1 deletion cmd/syft/cli/packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
errs <- fmt.Errorf("failed to write SBOM: %w", err)
return
}

}()
return errs
}
Expand Down
1 change: 1 addition & 0 deletions cmd/syft/cli/poweruser.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func PowerUser(v *viper.Viper, app *config.Application, ro *options.RootOptions)
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
checkForApplicationUpdate()
// TODO: this is broke, the bus isn't available yet
}
return poweruser.Run(cmd.Context(), app, args)
},
Expand Down
19 changes: 19 additions & 0 deletions cmd/syft/cli/ui/__snapshots__/handle_attestation_test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

[TestHandler_handleAttestationStarted/attesting_in_progress/task_line - 1]
Creating a thing running a thing
---

[TestHandler_handleAttestationStarted/attesting_in_progress/log - 1]
░░ contents
░░ of
░░ stuff!

---

[TestHandler_handleAttestationStarted/attesting_complete/task_line - 1]
Created a thing running a thing
---

[TestHandler_handleAttestationStarted/attesting_complete/log - 1]

---
16 changes: 16 additions & 0 deletions cmd/syft/cli/ui/__snapshots__/handle_cataloger_task_test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

[TestHandler_handleCatalogerTaskStarted/cataloging_task_in_progress - 1]
some task title [some value]
---

[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_in_progress - 1]
└── some task title [some value]
---

[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_complete - 1]
✔ └── some task done [some value]
---

[TestHandler_handleCatalogerTaskStarted/cataloging_sub_task_complete_with_removal - 1]

---
8 changes: 8 additions & 0 deletions cmd/syft/cli/ui/__snapshots__/handle_fetch_image_test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

[TestHandler_handleFetchImage/fetch_image_in_progress - 1]
Loading image ━━━━━━━━━━━━━━━━━━━━ [current] the-image
---

[TestHandler_handleFetchImage/fetch_image_complete - 1]
Loaded image the-image
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

[TestHandler_handleFileDigestsCatalogerStarted/cataloging_in_progress - 1]
Cataloging file digests ━━━━━━━━━━━━━━━━━━━━ [current]
---

[TestHandler_handleFileDigestsCatalogerStarted/cataloging_complete - 1]
Cataloged file digests
---
8 changes: 8 additions & 0 deletions cmd/syft/cli/ui/__snapshots__/handle_file_indexing_test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

[TestHandler_handleFileIndexingStarted/cataloging_in_progress - 1]
Indexing file system ━━━━━━━━━━━━━━━━━━━━ [current] /some/path
---

[TestHandler_handleFileIndexingStarted/cataloging_complete - 1]
Indexed file system /some/path
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

[TestHandler_handleFileMetadataCatalogerStarted/cataloging_in_progress - 1]
Cataloging file metadata ━━━━━━━━━━━━━━━━━━━━ [current]
---

[TestHandler_handleFileMetadataCatalogerStarted/cataloging_complete - 1]
Cataloged file metadata
---
16 changes: 16 additions & 0 deletions cmd/syft/cli/ui/__snapshots__/handle_package_cataloger_test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

[TestHandler_handlePackageCatalogerStarted/cataloging_in_progress - 1]
Cataloging packages [50 packages]
---

[TestHandler_handlePackageCatalogerStarted/cataloging_only_files_complete - 1]
Cataloging packages [50 packages]
---

[TestHandler_handlePackageCatalogerStarted/cataloging_only_packages_complete - 1]
Cataloging packages [100 packages]
---

[TestHandler_handlePackageCatalogerStarted/cataloging_complete - 1]
Cataloged packages [100 packages]
---
12 changes: 12 additions & 0 deletions cmd/syft/cli/ui/__snapshots__/handle_pull_docker_image_test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

[Test_dockerPullStatusFormatter_Render/pulling - 1]
3 Layers▕▅▃ ▏[12 B / 30 B]
---

[Test_dockerPullStatusFormatter_Render/download_complete - 1]
3 Layers▕█▃ ▏[30 B] Extracting...
---

[Test_dockerPullStatusFormatter_Render/complete - 1]
3 Layers▕███▏[30 B] Extracting...
---
8 changes: 8 additions & 0 deletions cmd/syft/cli/ui/__snapshots__/handle_read_image_test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

[TestHandler_handleReadImage/read_image_in_progress - 1]
Parsing image ━━━━━━━━━━━━━━━━━━━━ id
---

[TestHandler_handleReadImage/read_image_complete - 1]
Parsed image id
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

[TestHandler_handleSecretsCatalogerStarted/cataloging_in_progress - 1]
Cataloging secrets ━━━━━━━━━━━━━━━━━━━━ [64 secrets]
---

[TestHandler_handleSecretsCatalogerStarted/cataloging_complete - 1]
Cataloged secrets [64 secrets]
---
19 changes: 9 additions & 10 deletions cmd/syft/cli/ui/handle_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/google/uuid"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/zyedidia/generic/queue"
Expand Down Expand Up @@ -42,7 +43,7 @@ type attestLogFrame struct {
// attestLogFrameTickMsg indicates that the timer has ticked and we should render a frame.
type attestLogFrameTickMsg struct {
Time time.Time
sequence int
Sequence int
ID uint32
}

Expand Down Expand Up @@ -92,20 +93,18 @@ func (m *Handler) handleAttestationStarted(e partybus.Event) []tea.Model {

return []tea.Model{
tsk,
newLogFrame(newBackgroundLineReader(m.state.Running, reader, &stage), prog, borderStyle),
newLogFrame(newBackgroundLineReader(m.Running, reader, &stage), prog, borderStyle),
}
}

func newLogFrame(reader cosignOutputReader, prog progress.Progressable, borderStyle lipgloss.Style) *attestLogFrame {
lf := &attestLogFrame{
func newLogFrame(reader cosignOutputReader, prog progress.Progressable, borderStyle lipgloss.Style) attestLogFrame {
return attestLogFrame{
reader: reader,
prog: prog,
id: nextID(),
id: uuid.Must(uuid.NewUUID()).ID(),
updateDuration: 250 * time.Millisecond,
borderStype: borderStyle,
}

return lf
}

func newBackgroundLineReader(wg *sync.WaitGroup, reader io.Reader, stage *progress.Stage) *backgroundLineReader {
Expand Down Expand Up @@ -178,7 +177,7 @@ func (l attestLogFrame) Init() tea.Cmd {
// will ignore messages that don't contain ID by default.
ID: l.id,

sequence: l.sequence,
Sequence: l.sequence,
}
}
}
Expand Down Expand Up @@ -227,7 +226,7 @@ func (l attestLogFrame) queueNextTick() tea.Cmd {
return attestLogFrameTickMsg{
Time: t,
ID: l.id,
sequence: l.sequence,
Sequence: l.sequence,
}
})
}
Expand All @@ -241,7 +240,7 @@ func (l *attestLogFrame) handleTick(msg attestLogFrameTickMsg) tea.Cmd {
// If a sequence is set, and it's not the one we expect, reject the message.
// This prevents the log frame from receiving too many messages and
// thus updating too frequently.
if msg.sequence > 0 && msg.sequence != l.sequence {
if msg.Sequence > 0 && msg.Sequence != l.sequence {
return nil
}

Expand Down
Loading

0 comments on commit a15a9db

Please sign in to comment.