Skip to content

Commit

Permalink
feat(js): add compatibility mode field to archive export
Browse files Browse the repository at this point in the history
Resolves: grafana#1206 (comment)

In order to be able to create a new test Bundle from both a filesystem
*and* custom RuntimeOptions, this required some DRYing of the
`getSimpleBundle*` test helper functions.
  • Loading branch information
Ivan Mirić committed Nov 12, 2019
1 parent 586d0a0 commit 6760c9f
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 82 deletions.
43 changes: 26 additions & 17 deletions js/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ type Bundle struct {

BaseInitContext *InitContext

Env map[string]string
Env map[string]string
CompatibilityMode compiler.CompatibilityMode
}

// A BundleInstance is a self-contained instance of a Bundle.
Expand Down Expand Up @@ -80,7 +81,8 @@ func NewBundle(src *loader.SourceData, filesystems map[string]afero.Fs, rtOpts l
Program: pgm,
BaseInitContext: NewInitContext(rt, c, compatMode, new(context.Context),
filesystems, loader.Dir(src.URL)),
Env: rtOpts.Env,
Env: rtOpts.Env,
CompatibilityMode: compatMode,
}
if err := bundle.instantiate(rt, bundle.BaseInitContext); err != nil {
return nil, err
Expand Down Expand Up @@ -158,13 +160,19 @@ func NewBundleFromArchive(arc *lib.Archive, rtOpts lib.RuntimeOptions) (*Bundle,
env[k] = v
}

cm, err := compiler.CompatibilityModeString(arc.CompatibilityMode)
if err != nil {
return nil, err
}

bundle := &Bundle{
Filename: arc.FilenameURL,
Source: string(arc.Data),
Program: pgm,
Options: arc.Options,
BaseInitContext: initctx,
Env: env,
Filename: arc.FilenameURL,
Source: string(arc.Data),
Program: pgm,
Options: arc.Options,
BaseInitContext: initctx,
Env: env,
CompatibilityMode: cm,
}
if err := bundle.instantiate(bundle.BaseInitContext.runtime, bundle.BaseInitContext); err != nil {
return nil, err
Expand All @@ -174,15 +182,16 @@ func NewBundleFromArchive(arc *lib.Archive, rtOpts lib.RuntimeOptions) (*Bundle,

func (b *Bundle) makeArchive() *lib.Archive {
arc := &lib.Archive{
Type: "js",
Filesystems: b.BaseInitContext.filesystems,
Options: b.Options,
FilenameURL: b.Filename,
Data: []byte(b.Source),
PwdURL: b.BaseInitContext.pwd,
Env: make(map[string]string, len(b.Env)),
K6Version: consts.Version,
Goos: runtime.GOOS,
Type: "js",
Filesystems: b.BaseInitContext.filesystems,
Options: b.Options,
FilenameURL: b.Filename,
Data: []byte(b.Source),
PwdURL: b.BaseInitContext.pwd,
Env: make(map[string]string, len(b.Env)),
CompatibilityMode: b.CompatibilityMode.String(),
K6Version: consts.Version,
Goos: runtime.GOOS,
}
// Copy env so changes in the archive are not reflected in the source Bundle
for k, v := range b.Env {
Expand Down
137 changes: 78 additions & 59 deletions js/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,34 +42,31 @@ import (
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/guregu/null.v3"
null "gopkg.in/guregu/null.v3"
)

const isWindows = runtime.GOOS == "windows"

func getSimpleBundle(filename, data string) (*Bundle, error) {
return getSimpleBundleWithFs(filename, data, afero.NewMemMapFs())
}

func getSimpleBundleWithOptions(filename, data string, options lib.RuntimeOptions) (*Bundle, error) {
return NewBundle(
&loader.SourceData{
URL: &url.URL{Path: filename, Scheme: "file"},
Data: []byte(data),
},
map[string]afero.Fs{"file": afero.NewMemMapFs(), "https": afero.NewMemMapFs()},
options,
func getSimpleBundle(filename, data string, opts ...interface{}) (*Bundle, error) {
var (
fs = afero.NewMemMapFs()
rtOpts = lib.RuntimeOptions{}
)
}

func getSimpleBundleWithFs(filename, data string, fs afero.Fs) (*Bundle, error) {
for _, o := range opts {
switch opt := o.(type) {
case afero.Fs:
fs = opt
case lib.RuntimeOptions:
rtOpts = opt
}
}
return NewBundle(
&loader.SourceData{
URL: &url.URL{Path: filename, Scheme: "file"},
Data: []byte(data),
},
map[string]afero.Fs{"file": fs, "https": afero.NewMemMapFs()},
lib.RuntimeOptions{},
rtOpts,
)
}

Expand Down Expand Up @@ -117,7 +114,7 @@ func TestNewBundle(t *testing.T) {
t.Run("CompatibilityModeBase", func(t *testing.T) {
t.Run("ok/Minimal", func(t *testing.T) {
rtOpts := lib.RuntimeOptions{CompatibilityMode: null.StringFrom(compiler.CompatibilityModeBase.String())}
_, err := getSimpleBundleWithOptions("/script.js", `module.exports.default = function() {};`, rtOpts)
_, err := getSimpleBundle("/script.js", `module.exports.default = function() {};`, rtOpts)
assert.NoError(t, err)
})
t.Run("err", func(t *testing.T) {
Expand All @@ -142,7 +139,7 @@ func TestNewBundle(t *testing.T) {
tc := tc
t.Run(tc.name, func(t *testing.T) {
rtOpts := lib.RuntimeOptions{CompatibilityMode: null.StringFrom(tc.compatMode)}
_, err := getSimpleBundleWithOptions("/script.js", tc.code, rtOpts)
_, err := getSimpleBundle("/script.js", tc.code, rtOpts)
assert.EqualError(t, err, tc.expErr)
})
}
Expand Down Expand Up @@ -409,7 +406,7 @@ func TestNewBundle(t *testing.T) {
}

func getArchive(data string, rtOpts lib.RuntimeOptions) (*lib.Archive, error) {
b, err := getSimpleBundleWithOptions("script.js", data, rtOpts)
b, err := getSimpleBundle("script.js", data, rtOpts)
if err != nil {
return nil, err
}
Expand All @@ -419,28 +416,29 @@ func getArchive(data string, rtOpts lib.RuntimeOptions) (*lib.Archive, error) {
func TestNewBundleFromArchive(t *testing.T) {
t.Run("ok", func(t *testing.T) {
testCases := []struct {
name string
code string
rtOpts lib.RuntimeOptions
cm compiler.CompatibilityMode
code string
}{
{"MinimalExtended", `export let options = { vus: 12345 };
export default function() { return "hi!"; };`,
lib.RuntimeOptions{}},
{"MinimalBase", `module.exports.options = { vus: 12345 };
module.exports.default = function() { return "hi!" };`,
lib.RuntimeOptions{CompatibilityMode: null.StringFrom(compiler.CompatibilityModeBase.String())}},
{compiler.CompatibilityModeExtended, `
export let options = { vus: 12345 };
export default function() { return "hi!"; };`},
{compiler.CompatibilityModeBase, `
module.exports.options = { vus: 12345 };
module.exports.default = function() { return "hi!" };`},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
arc, err := getArchive(tc.code, tc.rtOpts)
t.Run(tc.cm.String(), func(t *testing.T) {
rtOpts := lib.RuntimeOptions{CompatibilityMode: null.StringFrom(tc.cm.String())}
arc, err := getArchive(tc.code, rtOpts)
assert.NoError(t, err)
b, err := NewBundleFromArchive(arc, tc.rtOpts)
b, err := NewBundleFromArchive(arc, rtOpts)
if !assert.NoError(t, err) {
return
}
assert.Equal(t, lib.Options{VUs: null.IntFrom(12345)}, b.Options)
assert.Equal(t, tc.cm, b.CompatibilityMode)

bi, err := b.Instantiate()
if !assert.NoError(t, err) {
Expand Down Expand Up @@ -575,7 +573,7 @@ func TestOpen(t *testing.T) {
export let file = open("` + openPath + `");
export default function() { return file };`

sourceBundle, err := getSimpleBundleWithFs(filepath.ToSlash(filepath.Join(prefix, pwd, "script.js")), data, fs)
sourceBundle, err := getSimpleBundle(filepath.ToSlash(filepath.Join(prefix, pwd, "script.js")), data, fs)
if tCase.isError {
assert.Error(t, err)
return
Expand Down Expand Up @@ -674,7 +672,7 @@ func TestBundleEnv(t *testing.T) {
if (__ENV.TEST_B !== "") { throw new Error("Invalid TEST_B: " + __ENV.TEST_B); }
}
`
b1, err := getSimpleBundleWithOptions("/script.js", data, rtOpts)
b1, err := getSimpleBundle("/script.js", data, rtOpts)
if !assert.NoError(t, err) {
return
}
Expand All @@ -701,35 +699,56 @@ func TestBundleEnv(t *testing.T) {

func TestBundleMakeArchive(t *testing.T) {
t.Run("ok", func(t *testing.T) {
fs := afero.NewMemMapFs()
_ = fs.MkdirAll("/path/to", 0755)
_ = afero.WriteFile(fs, "/path/to/file.txt", []byte(`hi`), 0644)
_ = afero.WriteFile(fs, "/path/to/exclaim.js", []byte(`export default function(s) { return s + "!" };`), 0644)
data := `
import exclaim from "./exclaim.js";
export let options = { vus: 12345 };
export let file = open("./file.txt");
export default function() { return exclaim(file); };
`
b, err := getSimpleBundleWithFs("/path/to/script.js", data, fs)
assert.NoError(t, err)
testCases := []struct {
cm compiler.CompatibilityMode
script string
exclaim string
}{
{compiler.CompatibilityModeExtended, `
import exclaim from "./exclaim.js";
export let options = { vus: 12345 };
export let file = open("./file.txt");
export default function() { return exclaim(file); };`,
`export default function(s) { return s + "!" };`},
{compiler.CompatibilityModeBase, `
var exclaim = require("./exclaim.js");
module.exports.options = { vus: 12345 };
module.exports.file = open("./file.txt");
module.exports.default = function() { return exclaim(module.exports.file); };`,
`module.exports.default = function(s) { return s + "!" };`},
}

arc := b.makeArchive()
for _, tc := range testCases {
tc := tc
t.Run(tc.cm.String(), func(t *testing.T) {
fs := afero.NewMemMapFs()
_ = fs.MkdirAll("/path/to", 0755)
_ = afero.WriteFile(fs, "/path/to/file.txt", []byte(`hi`), 0644)
_ = afero.WriteFile(fs, "/path/to/exclaim.js", []byte(tc.exclaim), 0644)

rtOpts := lib.RuntimeOptions{CompatibilityMode: null.StringFrom(tc.cm.String())}
b, err := getSimpleBundle("/path/to/script.js", tc.script, fs, rtOpts)
assert.NoError(t, err)

assert.Equal(t, "js", arc.Type)
assert.Equal(t, lib.Options{VUs: null.IntFrom(12345)}, arc.Options)
assert.Equal(t, "file:///path/to/script.js", arc.FilenameURL.String())
assert.Equal(t, data, string(arc.Data))
assert.Equal(t, "file:///path/to/", arc.PwdURL.String())
arc := b.makeArchive()

exclaimData, err := afero.ReadFile(arc.Filesystems["file"], "/path/to/exclaim.js")
assert.NoError(t, err)
assert.Equal(t, `export default function(s) { return s + "!" };`, string(exclaimData))
assert.Equal(t, "js", arc.Type)
assert.Equal(t, lib.Options{VUs: null.IntFrom(12345)}, arc.Options)
assert.Equal(t, "file:///path/to/script.js", arc.FilenameURL.String())
assert.Equal(t, tc.script, string(arc.Data))
assert.Equal(t, "file:///path/to/", arc.PwdURL.String())

fileData, err := afero.ReadFile(arc.Filesystems["file"], "/path/to/file.txt")
assert.NoError(t, err)
assert.Equal(t, `hi`, string(fileData))
assert.Equal(t, consts.Version, arc.K6Version)
exclaimData, err := afero.ReadFile(arc.Filesystems["file"], "/path/to/exclaim.js")
assert.NoError(t, err)
assert.Equal(t, tc.exclaim, string(exclaimData))

fileData, err := afero.ReadFile(arc.Filesystems["file"], "/path/to/file.txt")
assert.NoError(t, err)
assert.Equal(t, `hi`, string(fileData))
assert.Equal(t, consts.Version, arc.K6Version)
assert.Equal(t, tc.cm.String(), arc.CompatibilityMode)
})
}
})
t.Run("err", func(t *testing.T) {
testCases := []struct {
Expand Down
12 changes: 6 additions & 6 deletions js/initcontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,14 @@ func TestInitContextRequire(t *testing.T) {
t.Run("Invalid", func(t *testing.T) {
fs := afero.NewMemMapFs()
assert.NoError(t, afero.WriteFile(fs, "/file.js", []byte{0x00}, 0755))
_, err := getSimpleBundleWithFs("/script.js", `import "/file.js"; export default function() {}`, fs)
_, err := getSimpleBundle("/script.js", `import "/file.js"; export default function() {}`, fs)
require.Error(t, err)
assert.Contains(t, err.Error(), "SyntaxError: file:///file.js: Unexpected character '\x00' (1:0)\n> 1 | \x00\n")
})
t.Run("Error", func(t *testing.T) {
fs := afero.NewMemMapFs()
assert.NoError(t, afero.WriteFile(fs, "/file.js", []byte(`throw new Error("aaaa")`), 0755))
_, err := getSimpleBundleWithFs("/script.js", `import "/file.js"; export default function() {}`, fs)
_, err := getSimpleBundle("/script.js", `import "/file.js"; export default function() {}`, fs)
assert.EqualError(t, err, "Error: aaaa at file:///file.js:2:7(4)")
})

Expand Down Expand Up @@ -191,7 +191,7 @@ func TestInitContextRequire(t *testing.T) {
let v = fn();
export default function() {};`,
libName)
b, err := getSimpleBundleWithFs("/path/to/script.js", data, fs)
b, err := getSimpleBundle("/path/to/script.js", data, fs)
if !assert.NoError(t, err) {
return
}
Expand Down Expand Up @@ -220,7 +220,7 @@ func TestInitContextRequire(t *testing.T) {
throw new Error("myvar is set in global scope");
}
};`
b, err := getSimpleBundleWithFs("/script.js", data, fs)
b, err := getSimpleBundle("/script.js", data, fs)
if !assert.NoError(t, err) {
return
}
Expand Down Expand Up @@ -253,7 +253,7 @@ func createAndReadFile(t *testing.T, file string, content []byte, expectedLength
}
export default function() {}
`, file, binaryArg, expectedLength)
b, err := getSimpleBundleWithFs("/path/to/script.js", data, fs)
b, err := getSimpleBundle("/path/to/script.js", data, fs)

if !assert.NoError(t, err) {
return nil, err
Expand Down Expand Up @@ -349,7 +349,7 @@ func TestRequestWithBinaryFile(t *testing.T) {
assert.NoError(t, fs.MkdirAll("/path/to", 0755))
assert.NoError(t, afero.WriteFile(fs, "/path/to/file.bin", []byte("hi!"), 0644))

b, err := getSimpleBundleWithFs("/path/to/script.js",
b, err := getSimpleBundle("/path/to/script.js",
fmt.Sprintf(`
import http from "k6/http";
let binFile = open("/path/to/file.bin", "b");
Expand Down
2 changes: 2 additions & 0 deletions lib/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ type Archive struct {
// Environment variables
Env map[string]string `json:"env"`

CompatibilityMode string `json:"compatibilityMode"`

K6Version string `json:"k6version"`
Goos string `json:"goos"`
}
Expand Down

0 comments on commit 6760c9f

Please sign in to comment.