good morning!!!!

Skip to content
Snippets Groups Projects
runner.go 4.82 KiB
Newer Older
Garet Halliday's avatar
Garet Halliday committed
package test

import (
	"errors"
Garet Halliday's avatar
Garet Halliday committed
	"fmt"
Garet Halliday's avatar
Garet Halliday committed
	"io"

	"pggat/lib/bouncer/bouncers/v2"
Garet Halliday's avatar
Garet Halliday committed
	"pggat/lib/fed"
	packets "pggat/lib/fed/packets/v3.0"
Garet Halliday's avatar
Garet Halliday committed
	"pggat/lib/gat/pool/recipe"
Garet Halliday's avatar
Garet Halliday committed
	"pggat/lib/gsql"
Garet Halliday's avatar
Garet Halliday committed
	"pggat/lib/util/flip"
Garet Halliday's avatar
Garet Halliday committed
	"pggat/test/inst"
)

type Runner struct {
	config Config
	test   Test
}

func MakeRunner(config Config, test Test) Runner {
	return Runner{
		config: config,
		test:   test,
	}
}

Garet Halliday's avatar
Garet Halliday committed
func (T *Runner) prepare(client *gsql.Client, until int) []Capturer {
	results := make([]Capturer, until)
Garet Halliday's avatar
Garet Halliday committed

Garet Halliday's avatar
Garet Halliday committed
	for i := 0; i < until; i++ {
		x := T.test.Instructions[i]
Garet Halliday's avatar
Garet Halliday committed
		switch v := x.(type) {
		case inst.SimpleQuery:
			q := packets.Query(v)
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], q.IntoPacket(nil))
Garet Halliday's avatar
Garet Halliday committed
		case inst.Sync:
			client.Do(&results[i], fed.NewPacket(packets.TypeSync))
Garet Halliday's avatar
Garet Halliday committed
		case inst.Parse:
			p := packets.Parse{
				Destination: v.Destination,
				Query:       v.Query,
			}
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], p.IntoPacket(nil))
Garet Halliday's avatar
Garet Halliday committed
		case inst.Bind:
			p := packets.Bind{
				Destination: v.Destination,
				Source:      v.Source,
			}
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], p.IntoPacket(nil))
Garet Halliday's avatar
Garet Halliday committed
		case inst.DescribePortal:
			p := packets.Describe{
				Which:  'P',
				Target: string(v),
			}
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], p.IntoPacket(nil))
Garet Halliday's avatar
Garet Halliday committed
		case inst.DescribePreparedStatement:
			p := packets.Describe{
				Which:  'S',
				Target: string(v),
			}
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], p.IntoPacket(nil))
Garet Halliday's avatar
Garet Halliday committed
		case inst.Execute:
			p := packets.Execute{
				Target: string(v),
			}
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], p.IntoPacket(nil))
		case inst.ClosePortal:
			p := packets.Close{
				Which:  'P',
				Target: string(v),
			}
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], p.IntoPacket(nil))
		case inst.ClosePreparedStatement:
			p := packets.Close{
				Which:  'S',
				Target: string(v),
			}
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], p.IntoPacket(nil))
		case inst.CopyData:
			p := packets.CopyData(v)
Garet Halliday's avatar
Garet Halliday committed
			client.Do(&results[i], p.IntoPacket(nil))
		case inst.CopyDone:
			client.Do(&results[i], fed.NewPacket(packets.TypeCopyDone))
Garet Halliday's avatar
Garet Halliday committed
		}
Garet Halliday's avatar
Garet Halliday committed
	}

Garet Halliday's avatar
Garet Halliday committed
	return results
Garet Halliday's avatar
Garet Halliday committed
}

Garet Halliday's avatar
Garet Halliday committed
func (T *Runner) runModeL1(dialer recipe.Dialer, client *gsql.Client) error {
	server, _, err := dialer.Dial()
Garet Halliday's avatar
Garet Halliday committed
	if err != nil {
Garet Halliday's avatar
Garet Halliday committed
		return err
Garet Halliday's avatar
Garet Halliday committed
	}
	defer func() {
		_ = server.Close()
Garet Halliday's avatar
Garet Halliday committed
	}()

	for {
		var p fed.Packet
Garet Halliday's avatar
Garet Halliday committed
		p, err = client.ReadPacket(true, p)
Garet Halliday's avatar
Garet Halliday committed
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
Garet Halliday's avatar
Garet Halliday committed
			return err
Garet Halliday's avatar
Garet Halliday committed
		}
Garet Halliday's avatar
Garet Halliday committed
		_, clientErr, serverErr := bouncers.Bounce(client, server, p)
Garet Halliday's avatar
Garet Halliday committed
		if clientErr != nil {
Garet Halliday's avatar
Garet Halliday committed
			return clientErr
Garet Halliday's avatar
Garet Halliday committed
		}
		if serverErr != nil {
Garet Halliday's avatar
Garet Halliday committed
			return serverErr
Garet Halliday's avatar
Garet Halliday committed
	return nil
}

Garet Halliday's avatar
Garet Halliday committed
func (T *Runner) runModeOnce(dialer recipe.Dialer) ([]Capturer, error) {
Garet Halliday's avatar
Garet Halliday committed
	var client gsql.Client
	results := T.prepare(&client, len(T.test.Instructions))
	if err := client.Close(); err != nil {
		return nil, err
	}

	if err := T.runModeL1(dialer, &client); err != nil {
		return nil, err
	}

Garet Halliday's avatar
Garet Halliday committed
	return results, nil
}
Garet Halliday's avatar
Garet Halliday committed

Garet Halliday's avatar
Garet Halliday committed
func (T *Runner) runModeFail(dialer recipe.Dialer) error {
Garet Halliday's avatar
Garet Halliday committed
	for i := 1; i < len(T.test.Instructions)+1; i++ {
		var client gsql.Client
		T.prepare(&client, i)
		if err := client.Close(); err != nil {
			return err
		}

		if err := T.runModeL1(dialer, &client); err != nil && !errors.Is(err, io.EOF) {
			return err
		}
	}

	return nil
}

Garet Halliday's avatar
Garet Halliday committed
func (T *Runner) runMode(dialer recipe.Dialer) ([]Capturer, error) {
Garet Halliday's avatar
Garet Halliday committed
	instances := T.config.Stress
	if instances < 1 || T.test.SideEffects {
Garet Halliday's avatar
Garet Halliday committed
		return T.runModeOnce(dialer)
Garet Halliday's avatar
Garet Halliday committed
	}

	expected, err := T.runModeOnce(dialer)
	if err != nil {
		return nil, err
	}

Garet Halliday's avatar
Garet Halliday committed
	// fail testing
	if err = T.runModeFail(dialer); err != nil {
		return nil, err
	}

	// stress test
Garet Halliday's avatar
Garet Halliday committed
	var b flip.Bank

	for i := 0; i < instances-1; i++ {
		b.Queue(func() error {
			actual, err := T.runModeOnce(dialer)
			if err != nil {
				return err
			}
			if len(expected) != len(actual) {
				return fmt.Errorf("wrong number of results! expected %d but got %d", len(expected), len(actual))
			}
			for i, exp := range expected {
				act := actual[i]
				if err = exp.Check(&act); err != nil {
					return err
				}
			}
			return nil
		})
	}

Garet Halliday's avatar
Garet Halliday committed
	if err = b.Wait(); err != nil {
		return nil, err
	}

	return expected, nil
Garet Halliday's avatar
Garet Halliday committed
func (T *Runner) Run() error {
Garet Halliday's avatar
Garet Halliday committed
	var errs []error

	var expected []Capturer

Garet Halliday's avatar
Garet Halliday committed
	// modes
	for name, mode := range T.config.Modes {
		actual, err := T.runMode(mode)
		if err != nil {
Garet Halliday's avatar
Garet Halliday committed
			errs = append(errs, ErrorIn{
				Name: name,
				Err:  err,
			})
			continue
		if expected == nil {
			expected = actual
			continue
		}

Garet Halliday's avatar
Garet Halliday committed
		if len(expected) != len(actual) {
Garet Halliday's avatar
Garet Halliday committed
			errs = append(errs, ErrorIn{
				Name: name,
				Err:  fmt.Errorf("wrong number of results! expected %d but got %d", len(expected), len(actual)),
			})
			continue
Garet Halliday's avatar
Garet Halliday committed
		var modeErrs []error

Garet Halliday's avatar
Garet Halliday committed
		for i, exp := range expected {
			act := actual[i]

			if err = exp.Check(&act); err != nil {
				modeErrs = append(modeErrs, fmt.Errorf("instruction %d: %s", i+1, err.Error()))
Garet Halliday's avatar
Garet Halliday committed
			}
		}
Garet Halliday's avatar
Garet Halliday committed
		if len(modeErrs) > 0 {
			errs = append(errs, ErrorIn{
				Name: name,
				Err:  Errors(modeErrs),
			})
		}
Garet Halliday's avatar
Garet Halliday committed
	}

Garet Halliday's avatar
Garet Halliday committed
	if len(errs) > 0 {
		return Errors(errs)
	}
Garet Halliday's avatar
Garet Halliday committed
	return nil
}