Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
alixander committed Nov 6, 2023
1 parent 8c821e2 commit f9be104
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 107 deletions.
3 changes: 0 additions & 3 deletions d2cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package d2cli

import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -491,8 +490,6 @@ func compile(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin, la
default:
compileDur := time.Since(start)
if animateInterval <= 0 {
b, _ := json.MarshalIndent(diagram, "", " ")
println("\033[1;31m--- DEBUG:", string(b), "\033[m")
// Rename all the "root.layers.x" to the paths that the boards get output to
linkToOutput, err := resolveLinks("root", outputPath, diagram)
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions d2target/d2target.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ type Diagram struct {
}

// boardPath comes in the form of "x/layers/z/scenarios/a"
// or in the form of "layers/z/scenarios/a"
// or "layers/z/scenarios/a"
// or "x/z/a"
func (d *Diagram) GetBoard(boardPath string) *Diagram {
path := strings.Split(boardPath, string(os.PathSeparator))
if len(path) == 0 || len(boardPath) == 0 {
Expand Down Expand Up @@ -127,17 +128,17 @@ func (d *Diagram) getBoard(boardPath []string) *Diagram {

for _, b := range d.Layers {
if b.Name == head {
return b.getBoard(boardPath[2:])
return b.getBoard(boardPath[1:])
}
}
for _, b := range d.Scenarios {
if b.Name == head {
return b.getBoard(boardPath[2:])
return b.getBoard(boardPath[1:])
}
}
for _, b := range d.Steps {
if b.Name == head {
return b.getBoard(boardPath[2:])
return b.getBoard(boardPath[1:])
}
}
return nil
Expand Down
226 changes: 177 additions & 49 deletions e2etests-cli/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package e2etests_cli
import (
"bytes"
"context"
"errors"
"fmt"
"net/http"
"os"
Expand All @@ -12,8 +13,8 @@ import (
"testing"
"time"

"github.com/davecgh/go-spew/spew"
"nhooyr.io/websocket"

"oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/util-go/diff"
"oss.terrastruct.com/util-go/xmain"
Expand Down Expand Up @@ -550,11 +551,11 @@ i used to read
},
},
{
name: "watch",
name: "watch-regular",
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
writeFile(t, dir, "index.d2", `
a -> b
b.link: cream
b.link: layers.cream
layers: {
cream: {
Expand All @@ -565,59 +566,151 @@ layers: {
tms := testMain(dir, env, "--watch", "--browser=0", "index.d2")
tms.Stderr = stderr

doneChan := make(chan struct{}, 1)

tms.Start(t, ctx)
defer tms.Cleanup(t)
defer func() {
// Manually close, since watcher is daemon
err := tms.Signal(ctx, os.Interrupt)
assert.Success(t, err)
}()

go tms.Wait(ctx)
// Wait for watch server to spin up and listen
urlRE := regexp.MustCompile(`127.0.0.1:([0-9]+)`)
watchURL := waitLogs(ctx, stderr, urlRE)

if watchURL == "" {
t.Error(errors.New(string(stderr.Bytes())))
}
stderr.Reset()

// Start a client
c, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/watch", watchURL), nil)
assert.Success(t, err)
defer c.CloseNow()

// Get the link
_, msg, err := c.Read(ctx)
assert.Success(t, err)
aRE := regexp.MustCompile(`href=\\"([^\"]*)\\"`)
match := aRE.FindSubmatch(msg)
assert.Equal(t, 2, len(match))
linkedPath := match[1]

err = getWatchPage(ctx, t, fmt.Sprintf("http://%s/%s", watchURL, linkedPath))
assert.Success(t, err)

successRE := regexp.MustCompile(`broadcasting update to 1 client`)
line := waitLogs(ctx, stderr, successRE)
assert.NotEqual(t, "", line)
},
},
{
name: "watch-ok-link",
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
// This link technically works because D2 interprets it as a URL,
// and on local filesystem, that is whe path where the compilation happens
// to output it to.
writeFile(t, dir, "index.d2", `
a -> b
b.link: cream
ticker := time.NewTicker(100 * time.Millisecond)
layers: {
cream: {
c -> b
}
}`)
stderr := &bytes.Buffer{}
tms := testMain(dir, env, "--watch", "--browser=0", "index.d2")
tms.Stderr = stderr

tms.Start(t, ctx)
defer func() {
// Manually close, since watcher is daemon
err := tms.Signal(ctx, os.Interrupt)
assert.Success(t, err)
}()

// Wait for watch server to spin up and listen
urlRE := regexp.MustCompile(`127.0.0.1:([0-9]+)`)
compiled := false
go func() {
var url string
for i := 0; i < 10 && url == ""; i++ {
select {
case <-ticker.C:
out := string(stderr.Bytes())
url = urlRE.FindString(out)
compiled, _ = regexp.MatchString(`failed to recompile`, out)
println("\033[1;31m--- DEBUG:", compiled, "\033[m")
case <-ctx.Done():
ticker.Stop()
return
}
}

if url != "" {
c, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/watch", url), nil)
assert.Success(t, err)

req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("http://%s/cream", url), nil)
assert.Success(t, err)
var httpClient = &http.Client{}
resp, err := httpClient.Do(req)
assert.Success(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)

time.Sleep(1000)
spew.Dump(string(stderr.Bytes()))

_, _, err = c.Read(ctx)
spew.Dump(err)

defer c.Close(websocket.StatusNormalClosure, "")
}

doneChan <- struct{}{}
watchURL := waitLogs(ctx, stderr, urlRE)

if watchURL == "" {
t.Error(errors.New(string(stderr.Bytes())))
}
stderr.Reset()

// Start a client
c, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/watch", watchURL), nil)
assert.Success(t, err)
defer c.CloseNow()

// Get the link
_, msg, err := c.Read(ctx)
assert.Success(t, err)
aRE := regexp.MustCompile(`href=\\"([^\"]*)\\"`)
match := aRE.FindSubmatch(msg)
assert.Equal(t, 2, len(match))
linkedPath := match[1]

err = getWatchPage(ctx, t, fmt.Sprintf("http://%s/%s", watchURL, linkedPath))
assert.Success(t, err)

successRE := regexp.MustCompile(`broadcasting update to 1 client`)
line := waitLogs(ctx, stderr, successRE)
assert.NotEqual(t, "", line)
},
},
{
name: "watch-bad-link",
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
// Just verify we don't crash even with a bad link (it's treated as a URL, which users might have locally)
writeFile(t, dir, "index.d2", `
a -> b
b.link: dream
layers: {
cream: {
c -> b
}
}`)
stderr := &bytes.Buffer{}
tms := testMain(dir, env, "--watch", "--browser=0", "index.d2")
tms.Stderr = stderr

tms.Start(t, ctx)
defer func() {
// Manually close, since watcher is daemon
err := tms.Signal(ctx, os.Interrupt)
assert.Success(t, err)
}()

<-doneChan
// Wait for watch server to spin up and listen
urlRE := regexp.MustCompile(`127.0.0.1:([0-9]+)`)
watchURL := waitLogs(ctx, stderr, urlRE)

if watchURL == "" {
t.Error(errors.New(string(stderr.Bytes())))
}
stderr.Reset()

// Start a client
c, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/watch", watchURL), nil)
assert.Success(t, err)
defer c.CloseNow()

err := tms.Signal(ctx, os.Interrupt)
assert.Error(t, err)
// Get the link
_, msg, err := c.Read(ctx)
assert.Success(t, err)
aRE := regexp.MustCompile(`href=\\"([^\"]*)\\"`)
match := aRE.FindSubmatch(msg)
assert.Equal(t, 2, len(match))
linkedPath := match[1]

err = getWatchPage(ctx, t, fmt.Sprintf("http://%s/%s", watchURL, linkedPath))
assert.Success(t, err)

successRE := regexp.MustCompile(`broadcasting update to 1 client`)
line := waitLogs(ctx, stderr, successRE)
assert.NotEqual(t, "", line)
},
},
}
Expand Down Expand Up @@ -716,3 +809,38 @@ func testdataIgnoreDiff(tb testing.TB, ext string, got []byte) {
func getNumBoards(svg string) int {
return strings.Count(svg, `class="d2`)
}

func waitLogs(ctx context.Context, buf *bytes.Buffer, pattern *regexp.Regexp) string {
ticker := time.NewTicker(10 * time.Millisecond)
var match string
for i := 0; i < 100 && match == ""; i++ {
select {
case <-ticker.C:
out := string(buf.Bytes())
match = pattern.FindString(out)
case <-ctx.Done():
ticker.Stop()
return ""
}
}

return match
}

func getWatchPage(ctx context.Context, t *testing.T, page string) error {
req, err := http.NewRequestWithContext(ctx, "GET", page, nil)
if err != nil {
return err
}

var httpClient = &http.Client{}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("status code: %d", resp.StatusCode)
}
return nil
}
3 changes: 1 addition & 2 deletions go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f9be104

Please sign in to comment.