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
}
LoadRawSuite with the given filename from fs.
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)
}