good morning!!!!

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

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

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

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 *fed.Conn, until int) error {
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
			if err := client.WritePacket(&q); err != nil {
				return err
			}
Garet Halliday's avatar
Garet Halliday committed
		case inst.Sync:
Garet Halliday's avatar
Garet Halliday committed
			if err := client.WritePacket(&packets.Sync{}); err != nil {
				return err
			}
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
			if err := client.WritePacket(&p); err != nil {
				return err
			}
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
			if err := client.WritePacket(&p); err != nil {
				return err
			}
Garet Halliday's avatar
Garet Halliday committed
		case inst.DescribePortal:
			p := packets.Describe{
Garet Halliday's avatar
Garet Halliday committed
				Which: 'P',
				Name:  string(v),
			}
			if err := client.WritePacket(&p); err != nil {
				return err
Garet Halliday's avatar
Garet Halliday committed
			}
		case inst.DescribePreparedStatement:
			p := packets.Describe{
Garet Halliday's avatar
Garet Halliday committed
				Which: 'S',
				Name:  string(v),
			}
			if err := client.WritePacket(&p); err != nil {
				return err
Garet Halliday's avatar
Garet Halliday committed
			}
		case inst.Execute:
			p := packets.Execute{
				Target: string(v),
			}
Garet Halliday's avatar
Garet Halliday committed
			if err := client.WritePacket(&p); err != nil {
				return err
			}
		case inst.ClosePortal:
			p := packets.Close{
Garet Halliday's avatar
Garet Halliday committed
				Which: 'P',
				Name:  string(v),
			}
			if err := client.WritePacket(&p); err != nil {
				return err
			}
		case inst.ClosePreparedStatement:
			p := packets.Close{
Garet Halliday's avatar
Garet Halliday committed
				Which: 'S',
				Name:  string(v),
			}
			if err := client.WritePacket(&p); err != nil {
				return err
		case inst.CopyData:
			p := packets.CopyData(v)
Garet Halliday's avatar
Garet Halliday committed
			if err := client.WritePacket(&p); err != nil {
				return err
			}
		case inst.CopyDone:
Garet Halliday's avatar
Garet Halliday committed
			if err := client.WritePacket(&packets.CopyDone{}); err != nil {
				return err
			}
Garet Halliday's avatar
Garet Halliday committed
		}
Garet Halliday's avatar
Garet Halliday committed
	}

Garet Halliday's avatar
Garet Halliday committed
	if err := client.WritePacket(&packets.Terminate{}); err != nil {
		return err
	}

	return client.Flush()
Garet Halliday's avatar
Garet Halliday committed
}

Garet Halliday's avatar
Garet Halliday committed
func (T *Runner) runModeL1(dialer recipe.Dialer, client *fed.Conn) 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
	client.Middleware = append(client.Middleware, unterminate.Unterminate)

Garet Halliday's avatar
Garet Halliday committed
	for {
		var p fed.Packet
Garet Halliday's avatar
Garet Halliday committed
		p, err = client.ReadPacket(true)
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) ([]byte, error) {
	inward, outward := gsql.NewPair()
	if err := T.prepare(inward, len(T.test.Instructions)); err != nil {
		return nil, err
	}

	if err := T.runModeL1(dialer, outward); err != nil {
Garet Halliday's avatar
Garet Halliday committed
		return nil, err
	}

Garet Halliday's avatar
Garet Halliday committed
	if err := inward.Close(); err != nil {
Garet Halliday's avatar
Garet Halliday committed
		return nil, err
	}

Garet Halliday's avatar
Garet Halliday committed
	return io.ReadAll(inward.NetConn)
Garet Halliday's avatar
Garet Halliday committed
}
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++ {
Garet Halliday's avatar
Garet Halliday committed
		inward, outward := gsql.NewPair()
		if err := T.prepare(inward, i); err != nil {
Garet Halliday's avatar
Garet Halliday committed
			return err
		}

Garet Halliday's avatar
Garet Halliday committed
		if err := T.runModeL1(dialer, outward); err != nil && !errors.Is(err, io.EOF) {
Garet Halliday's avatar
Garet Halliday committed
			return err
		}
	}

	return nil
}

Garet Halliday's avatar
Garet Halliday committed
func (T *Runner) runMode(dialer recipe.Dialer) ([]byte, 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
			}
Garet Halliday's avatar
Garet Halliday committed
			if !bytes.Equal(expected, actual) {
				return fmt.Errorf("mismatched results: expected %v but got %v", expected, actual)
Garet Halliday's avatar
Garet Halliday committed
			}
			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

Garet Halliday's avatar
Garet Halliday committed
	var expected []byte
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 !bytes.Equal(expected, actual) {
Garet Halliday's avatar
Garet Halliday committed
			errs = append(errs, ErrorIn{
				Name: name,
Garet Halliday's avatar
Garet Halliday committed
				Err:  fmt.Errorf("mismatched results: expected %v but got %v", expected, actual),
Garet Halliday's avatar
Garet Halliday committed
			})
			continue
Garet Halliday's avatar
Garet Halliday committed
		}
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
}