func LoadRawMock(filename string, fs FileSystem) (*RawMock, error) {
	raw, err := fs.Load(filename)
	if err != nil {
		return nil, err
	}
	x := &struct {
		Variables map[string]string
	}{}
	err = raw.decodeLaxTo(x)
	if err != nil {
		return nil, err  
	}
	return &RawMock{
		File:      raw,
		Variables: x.Variables,
	}, nil
}
LoadRawMock tries to reads filename from fs as a RawMock.
package suite
import (
	"fmt"
	"io/ioutil"
	"log"
	"path"
	"path/filepath"
	"strings"
	"github.com/vdobler/ht/cookiejar"
	"github.com/vdobler/ht/errorlist"
	"github.com/vdobler/ht/ht"
	"github.com/vdobler/ht/internal/hjson"
	"github.com/vdobler/ht/mock"
	"github.com/vdobler/ht/populate"
	"github.com/vdobler/ht/scope"
)
func pp(msg string, v interface{}) {
	data, err := hjson.Marshal(v)
	fmt.Println(msg, string(data), err)
}
type File struct {
	Data string
	Name string
}
func LoadFile(filename string) (*File, error) {
	filename = filepath.ToSlash(filename)
	filename = path.Clean(filename)
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	var soup interface{}
	err = hjson.Unmarshal(data, &soup)
	if err != nil {
		return nil, fmt.Errorf("file %s not valid hjson: %s", filename, err)
	}
	return &File{
		Data: string(data),
		Name: filepath.ToSlash(filename),
	}, nil
}
func (f *File) Dirname() string {
	return path.Dir(f.Name)
}
func (f *File) Basename() string {
	return path.Base(f.Name)
}
func (f *File) decode() (map[string]interface{}, error) {
	var soup interface{}
	err := hjson.Unmarshal([]byte(f.Data), &soup)
	if err != nil {
		return nil, fmt.Errorf("file %s is not valid hjson: %s", f.Name, err)
	}
	m, ok := soup.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("file %s is not an object (got %T)", f.Name, soup)
	}
	return m, nil
}
func (f *File) decodeLaxTo(x interface{}) error {
	var soup interface{}
	err := hjson.Unmarshal([]byte(f.Data), &soup)
	if err != nil {
		return fmt.Errorf("file %s is not valid hjson: %s", f.Name, err)
	}
	m, ok := soup.(map[string]interface{})
	if !ok {
		return fmt.Errorf("file %s is not an object (got %T)", f.Name, soup)
	}
	err = populate.Lax(x, m)
	if err != nil {
		return fmt.Errorf("error decoding file %s: %s", f.Name, err)
	}
	return nil
}
func (f *File) decodeStrictTo(x interface{}, drop []string) error {
	var soup interface{}
	err := hjson.Unmarshal([]byte(f.Data), &soup)
	if err != nil {
		return fmt.Errorf("file %s is not valid hjson: %s", f.Name, err)
	}
	m, ok := soup.(map[string]interface{})
	if !ok {
		return fmt.Errorf("file %s is not an object (got %T)", f.Name, soup)
	}
	for _, d := range drop {
		delete(m, d)
	}
	err = populate.Strict(x, m)
	if err != nil {
		return fmt.Errorf("error decoding file %s: %s", f.Name, err)
	}
	return nil
}
type Mixin struct {
	*File
}
func loadMixin(filename string, fs FileSystem) (*Mixin, error) {
	file, err := fs.Load(filename)
	if err != nil {
		return nil, err
	}
	return &Mixin{File: file}, nil
}
type RawTest struct {
	*File
	Mixins      []*Mixin           
	Variables   map[string]string  
	contextVars map[string]string
	mocks       []*RawMock
	disabled    bool
}
func (rt *RawTest) String() string {
	return rt.File.Name
}
func (rt *RawTest) Disable() { rt.disabled = true }
func (rt *RawTest) Enable() { rt.disabled = false }
func (rt *RawTest) IsEnabled() bool { return !rt.disabled }
func LoadRawTest(filename string, fs FileSystem) (*RawTest, error) {
	raw, err := fs.Load(filename)
	if err != nil {
		return nil, err
	}
	x := &struct {
		Mixin     []string
		Variables map[string]string
	}{}
	err = raw.decodeLaxTo(x)
	if err != nil {
		return nil, err
	}
	testdir := raw.Dirname()
	mixins, err := loadMixins(x.Mixin, testdir, fs)
	if err != nil {
		return nil, err
	}
	return &RawTest{
		File:      raw,
		Mixins:    mixins,
		Variables: x.Variables,
	}, nil
}
func loadMixins(mixs []string, dir string, fs FileSystem) ([]*Mixin, error) {
	mixins := []*Mixin{}
	for _, file := range mixs {
		mixpath := path.Join(dir, file)
		mixin, err := loadMixin(mixpath, fs)
		if err != nil {
			return nil, fmt.Errorf("cannot load mixin %q: %s",
				file, err)
		}
		mixins = append(mixins, mixin)
	}
	return mixins, nil
}
func (rt *RawTest) ToTest(variables scope.Variables) (*ht.Test, error) {
	bogus := &ht.Test{Result: ht.Result{Status: ht.Bogus}}
	replacer := variables.Replacer()
	substituted := &RawTest{
		File: &File{
			Data: replacer.Replace(rt.File.Data),
			Name: rt.File.Name,
		},
		Mixins: make([]*Mixin, len(rt.Mixins)),
	}
	for i := range rt.Mixins {
		substituted.Mixins[i] = &Mixin{
			File: &File{
				Data: replacer.Replace(rt.Mixins[i].File.Data),
				Name: rt.Mixins[i].File.Name,
			},
		}
	}
	test, err := substituted.toTest(variables)
	if err != nil {
		return bogus, err
	}
	mixins := make([]*ht.Test, len(substituted.Mixins))
	for i, rawmix := range substituted.Mixins {
		mix, err := rawmix.toTest()
		if err != nil {
			return bogus, err
		}
		mixins[i] = mix
	}
	origname, origdescr, origfollow := test.Name, test.Description, test.Request.FollowRedirects
	all := append([]*ht.Test{test}, mixins...)
	merged, err := ht.Merge(all...)
	if err != nil {
		return bogus, err
	}
	merged.Description = origdescr
	merged.Name = origname
	merged.Request.FollowRedirects = origfollow
	return merged, nil
}
func (m *Mixin) toTest() (*ht.Test, error) {
	rt := &RawTest{
		File: &File{
			Data: m.File.Data,
			Name: m.Name,
		},
	}
	return rt.toTest(nil)
}
func (rt *RawTest) toTest(variables map[string]string) (*ht.Test, error) {
	m, err := rt.File.decode()
	if err != nil {
		return nil, err
	}
	delete(m, "Mixin")
	test := &ht.Test{}
	err = populate.Strict(test, m)
	if err != nil {
		return nil, err  
	}
	test.Variables = make(map[string]string, len(variables))
	for n, v := range variables {
		test.Variables[n] = v
	}
	return test, nil
}
type RawElement struct {
	File      string
	Variables map[string]string
	Mocks     []string
	Test map[string]interface{}
}
type RawSuite struct {
	*File
	Name, Description     string
	Setup, Main, Teardown []RawElement
	KeepCookies           bool
	OmitChecks            bool
	Variables             map[string]string
	Verbosity             int
	tests []*RawTest
}
func (rs *RawSuite) RawTests() []*RawTest {
	return rs.tests
}
func (rs *RawSuite) AddRawTests(ts ...*RawTest) {
	rs.tests = append(rs.tests, ts...)
}
func parseRawSuite(name string, txt string) (*RawSuite, error) {
	fs, err := NewFileSystem(txt)
	if err != nil {
		return nil, err
	}
	return LoadRawSuite(name, fs)
}
func LoadRawSuite(filename string, fs FileSystem) (*RawSuite, error) {
	raw, err := fs.Load(filename)
	if err != nil {
		return nil, err
	}
	rs := &RawSuite{}
	err = raw.decodeStrictTo(rs, nil)
	if err != nil {
		return nil, err  
	}
	rs.File = raw  
	dir := rs.File.Dirname()
	load := func(elems []RawElement, which string) error {
		for i, elem := range elems {
			var err error
			var rt *RawTest
			var filename string
			if elem.File != "" {
				filename = path.Join(dir, elem.File)
				rt, err = LoadRawTest(filename, fs)
				if err != nil {
					return fmt.Errorf("cannot load test %q (%d. %s): %s",
						filename, i+1, which, err)
				}
			} else if len(elem.Test) != 0 {
				name := fmt.Sprintf("%s_inline-%d.%s",
					rs.File.Name, i+1, which)
				rt, err = rawTestFromInline(name, dir, fs, elem.Test)
				if err != nil {
					return fmt.Errorf("cannot parse inline test (%d. %s): %s",
						i+1, which, err)
				}
			} else {
				return fmt.Errorf("File and Test must not both be empty in %d. %s", i+1, which)
			}
			rt.contextVars = elem.Variables
			for _, mockname := range elem.Mocks {
				mf, err := LoadRawMock(path.Join(dir, mockname), fs)
				if err != nil {
					return fmt.Errorf("cannot instantiate test (%d. %s): cannot load mock %q: %s",
						i+1, which, mockname, err)
				}
				rt.mocks = append(rt.mocks, mf)
			}
			rs.tests = append(rs.tests, rt)
		}
		return nil
	}
	err = load(rs.Setup, "Setup")
	if err != nil {
		return nil, err
	}
	err = load(rs.Main, "Main")
	if err != nil {
		return nil, err
	}
	err = load(rs.Teardown, "Teardown")
	if err != nil {
		return nil, err
	}
	return rs, nil
}
func rawTestFromInline(name, dir string, fs FileSystem, inline map[string]interface{}) (*RawTest, error) {
	mixins := []*Mixin{}
	if m, ok := inline["Mixins"]; ok {
		mixs := []string{}
		err := populate.Strict(&mixs, m)
		if err != nil {
			fmt.Println("Mixins issue", err)
			return nil, err
		}
		delete(inline, "Mixins")
		mixins, err = loadMixins(mixs, dir, fs)
		if err != nil {
			return nil, err
		}
	}
	b, err := hjson.Marshal(inline)
	if err != nil {
		return nil, err
	}
	raw := &File{
		Data: string(b),
		Name: name,
	}
	return &RawTest{
		File:   raw,
		Mixins: mixins,
	}, nil
}
func (rs *RawSuite) Validate(global map[string]string) error {
	suiteScope := scope.New(global, rs.Variables, true)
	suiteScope["SUITE_DIR"] = rs.File.Dirname()
	suiteScope["SUITE_NAME"] = rs.File.Basename()
	el := errorlist.List{}
	for _, rt := range rs.tests {
		callScope := scope.New(suiteScope, rt.contextVars, true)
		testScope := scope.New(callScope, rt.Variables, false)
		testScope["TEST_DIR"] = rt.File.Dirname()
		testScope["TEST_NAME"] = rt.File.Basename()
		_, err := rt.ToTest(testScope)
		if err != nil {
			err := fmt.Errorf("invalid test %s (included by %s): %s",
				rt.File.Name, rs.File.Name, err)
			el = append(el, err)
		}
	}
	if len(el) > 0 {
		return el
	}
	return nil
}
func (rs *RawSuite) Execute(global map[string]string, jar *cookiejar.Jar, logger *log.Logger) *Suite {
	suite := NewFromRaw(rs, global, jar, logger)
	N := len(rs.tests)
	setup, main, teardown := len(rs.Setup), len(rs.Main), len(rs.Teardown)
	i := 0
	isSetup := func() bool { return i <= setup }
	isMain := func() bool { return i > setup && i <= setup+main }
	isSetupOrMain := func() bool { return i <= setup+main }
	setupfailures := false
	executor := func(test *ht.Test) error {
		i++
		if isSetup() {
			test.SetMetadata("SeqNo", fmt.Sprintf("Setup-%02d", i))
		} else if isMain() {
			test.SetMetadata("SeqNo", fmt.Sprintf("Main-%02d", i-setup))
		} else {
			test.SetMetadata("SeqNo", fmt.Sprintf("Teardown-%02d", i-setup-main))
		}
		switch {
		case test.Result.Status == ht.Skipped:
			fallthrough
		case !rs.tests[i-1].IsEnabled():
			fallthrough
		case setupfailures && isSetupOrMain():
			test.Result.Status = ht.Skipped
			return nil
		}
		if test.Result.Status != ht.Bogus {
			test.Execution.Verbosity = rs.Verbosity
			test.Run()
		}
		if test.Result.Status > ht.Pass && isSetup() {
			setupfailures = true
		}
		return nil
	}
	suite.Iterate(executor)
	status := ht.NotRun
	errors := errorlist.List{}
	for i := 0; i < N-teardown && i < len(suite.Tests); i++ {
		if ts := suite.Tests[i].Result.Status; ts > status {
			status = ts
		}
		if err := suite.Tests[i].Result.Error; err != nil {
			errors = append(errors, err)
		}
	}
	suite.Status = status
	if len(errors) == 0 {
		suite.Error = nil
	} else {
		suite.Error = errors
	}
	return suite
}
type FileSystem map[string]*File
func (fs FileSystem) Load(name string) (*File, error) {
	if len(fs) == 0 {
		return LoadFile(name)
	}
	if f, ok := fs[name]; ok {
		return f, nil
	}
	return nil, fmt.Errorf("file %s not found", name)
}
func NewFileSystem(txt string) (FileSystem, error) {
	txt = "\n" + txt
	parts := strings.Split(txt, "\n#")
	filesystem := make(FileSystem, len(parts))
	for i, part := range parts {
		part = strings.TrimSpace(part)
		if len(part) == 0 {
			continue
		}
		sp := strings.SplitN(part, "\n", 2)
		name := strings.TrimSpace(sp[0])
		if len(sp) != 2 || len(name) == 0 {
			return nil, fmt.Errorf("malformed part %d", i+1)
		}
		if _, ok := filesystem[name]; ok {
			return nil, fmt.Errorf("duplicate name %q", name)
		}
		filesystem[name] = &File{
			Name: name,
			Data: sp[1],
		}
	}
	return filesystem, nil
}
type RawScenario struct {
	Name       string             
	File       string             
	Percentage int                
	MaxThreads int                
	Variables  map[string]string  
	OmitChecks bool               
	rawSuite *RawSuite
}
type RawLoadTest struct {
	*File
	Name        string
	Description string
	Scenarios   []RawScenario
	Variables   map[string]string
}
func parseRawLoadtest(name string, txt string) (*RawLoadTest, error) {
	fs, err := NewFileSystem(txt)
	if err != nil {
		return nil, err
	}
	return LoadRawLoadtest(name, fs)
}
func LoadRawLoadtest(filename string, fs FileSystem) (*RawLoadTest, error) {
	raw, err := fs.Load(filename)
	if err != nil {
		return nil, err
	}
	rlt := &RawLoadTest{}
	err = raw.decodeStrictTo(rlt, nil)
	if err != nil {
		return nil, err  
	}
	rlt.File = raw  
	dir := rlt.File.Dirname()
	for i, s := range rlt.Scenarios {
		if s.File != "" {
			filename := path.Join(dir, s.File)
			rs, err := LoadRawSuite(filename, fs)
			if err != nil {
				return nil, fmt.Errorf("cannot load suite %q (%d. scenario): %s",
					filename, i+1, err)
			}
			rlt.Scenarios[i].rawSuite = rs
		} else {
			panic("File must not be empty")
		}
	}
	return rlt, nil
}
func (raw *RawLoadTest) ToScenario(globals map[string]string) []Scenario {
	scenarios := []Scenario{}
	ltscope := scope.New(globals, raw.Variables, true)
	for _, rs := range raw.Scenarios {
		callscope := scope.New(ltscope, rs.Variables, true)
		scen := Scenario{
			Name:       rs.Name,
			RawSuite:   rs.rawSuite,
			Percentage: rs.Percentage,
			MaxThreads: rs.MaxThreads,
			globals:    callscope,
		}
		scenarios = append(scenarios, scen)
	}
	return scenarios
}
type RawMock struct {
	*File
	Variables map[string]string
}