Commit f20355d8 authored by Boris Mühmer's avatar Boris Mühmer
Browse files

snapshot

parent 1aa3b703
Loading
Loading
Loading
Loading
+143 −62
Original line number Diff line number Diff line
@@ -15,17 +15,16 @@ const (
	Err Channel = 3
)

type Streams struct {
	match chan Match
	done  chan bool
}

type Expect struct {
	stdin       bytes.Buffer
	stdout      bytes.Buffer
	stderr      bytes.Buffer
	wg     sync.WaitGroup
	ss     map[Channel]Streams
	matchStream chan struct {
		c Channel
		m Match
	}
	terminateStream chan bool
	finishedStream  chan bool
}

type Match struct {
@@ -34,45 +33,32 @@ type Match struct {
}

func New() (*Expect, error) {
	matchOutStream := make(chan Match)
	startedOutStream := make(chan bool)
	doneOutStream := make(chan bool)
	ssOut := Streams{
		match: matchOutStream,
		done:  doneOutStream,
	}

	matchErrStream := make(chan Match)
	startedErrStream := make(chan bool)
	doneErrStream := make(chan bool)
	ssErr := Streams{
		match: matchErrStream,
		done:  doneErrStream,
	}

	ss := make(map[Channel]Streams)
	ss[Out] = ssOut
	ss[Err] = ssErr

	x := &Expect{
		ss: ss,
		matchStream: make(chan struct {
			c Channel
			m Match
		}),
		terminateStream: make(chan bool),
		finishedStream:  make(chan bool),
	}

	x.wg.Add(2)
	go process(&x.wg, &x.stdout, &x.stdin, startedOutStream, matchOutStream, doneOutStream)
	go process(&x.wg, &x.stderr, &x.stdin, startedErrStream, matchErrStream, doneErrStream)

	<-startedOutStream
	<-startedErrStream
	startedChannel := make(chan bool)
	defer close(startedChannel)
	go x.worker(startedChannel)
	<-startedChannel

	return x, nil
}

func (x *Expect) Close() error {
	for _, b := range x.ss {
		b.done <- true
func async(c chan<- bool, b bool) {
	go func() {
		c <- b
	}()
}
	x.wg.Wait()

func (x *Expect) Close() error {
	async(x.terminateStream, true)
	<-x.finishedStream
	return nil
}

@@ -89,19 +75,117 @@ func (x *Expect) Stderr() io.Writer {
}

func (x *Expect) Wait(c Channel, match, text string) error {
	x.ss[c].match <- Match{p: match, v: text}
	x.matchStream <- struct {
		c Channel
		m Match
	}{
		c: c,
		m: Match{p: match, v: text},
	}
	return nil
}

func (x *Expect) worker(started chan<- bool) {
	var wg sync.WaitGroup

	matchOutStream := make(chan Match)
	startedOutStream := make(chan bool)
	terminateOutStream := make(chan bool)
	terminatedOutStream := make(chan bool)
	terminatedOut := false

	matchErrStream := make(chan Match)
	startedErrStream := make(chan bool)
	terminateErrStream := make(chan bool)
	terminatedErrStream := make(chan bool)
	terminatedErr := false

	checkStream := make(chan bool)
	defer close(checkStream)

	wg.Add(2)
	go func() {
		defer wg.Done()
		process(&x.stdout, &x.stdin,
			startedOutStream, matchOutStream, terminateOutStream, terminatedOutStream)
	}()
	go func() {
		defer wg.Done()
		process(&x.stderr, &x.stdin,
			startedErrStream, matchErrStream, terminateErrStream, terminatedErrStream)
	}()

	<-startedOutStream
	<-startedErrStream

	started <- true

	for {
		select {
		case match, valid := <-x.matchStream:
			if !valid {
				continue
			}
			switch match.c {
			case Out:
				matchOutStream <- match.m
			case Err:
				matchErrStream <- match.m
			}
		case terminate, valid := <-x.terminateStream:
			if !valid {
				continue
			}
			if !terminate {
				continue
			}
			async(terminateOutStream, true)
			async(terminateErrStream, true)
		case terminated, valid := <-terminatedOutStream:
			if !valid {
				continue
			}
			if !terminated {
				continue
			}
			terminatedOut = true
			async(checkStream, true)
		case terminated, valid := <-terminatedErrStream:
			if !valid {
				continue
			}
			if !terminated {
				continue
			}
			terminatedErr = true
			async(checkStream, true)
		case check, valid := <-checkStream:
			if !valid {
				continue
			}
			if !check {
				continue
			}
			if !terminatedOut {
				continue
			}
			if !terminatedErr {
				continue
			}
			wg.Wait()
			x.finishedStream <- true
		}
	}
}

func process(
	wg *sync.WaitGroup,
	in io.Reader,
	out io.Writer,
	startedStream chan<- bool,
	matchStream <-chan Match,
	doneStream <-chan bool,
	terminateStream <-chan bool,
	terminatedStream chan<- bool,
) {
	defer wg.Done()
	matches := []Match{}

	startedStream <- true
@@ -113,18 +197,20 @@ func process(
				continue
			}
			matches = append(matches, match)
		case done, valid := <-doneStream:
		case terminate, valid := <-terminateStream:
			if !valid {
				continue
			}
			if !done {
			if !terminate {
				continue
			}
			terminatedStream <- true
			return
		default:
			bytes, err := io.ReadAll(in)
			switch err {
			case nil:
			if err != nil {
				continue
			}
			if len(bytes) == 0 {
				continue
			}
@@ -136,11 +222,6 @@ func process(
				io.WriteString(out, m.v)
				break
			}
			//case io.EOF:
			//	return
			default:
				continue
			}
		}
	}
}
+2 −2
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ const (
func TestPasswdChange(t *testing.T) {
	x, err := New()
	if err != nil {
		t.Fatalf("New() failed with: %s", err)
		t.Fatalf("get.New() failed with: %s", err)
	}
	defer x.Close()

@@ -34,7 +34,7 @@ func TestPasswdChange(t *testing.T) {

	p, err := passwd.New(x.Stdin(), x.Stdout(), x.Stderr(), lp2User)
	if err != nil {
		t.Fatalf("New() failed with: %s", err)
		t.Fatalf("passwd.New() failed with: %s", err)
	}

	if err := p.Run(); err != nil {
+3 −3
Original line number Diff line number Diff line
@@ -81,7 +81,7 @@ func (cmd *Passwd) Run() error {
	fmt.Fprintf(cmd.stderr, "Current password: ")
	var p0 string
	if _, err := fmt.Fscanf(cmd.stdin, "%s\n", &p0); err != nil {
		return err
		return fmt.Errorf("p0: %s", err)
	}
	if p0 != lp.Pass {
		t := "wrong password"
@@ -92,13 +92,13 @@ func (cmd *Passwd) Run() error {
	fmt.Fprintf(cmd.stderr, "New password: ")
	var p1 string
	if _, err := fmt.Fscanf(cmd.stdin, "%s\n", &p1); err != nil {
		return err
		return fmt.Errorf("p1: %s", err)
	}

	fmt.Fprintf(cmd.stderr, "Retype new password: ")
	var p2 string
	if _, err := fmt.Fscanf(cmd.stdin, "%s\n", &p2); err != nil {
		return err
		return fmt.Errorf("p2: %s", err)
	}
	if p1 != p2 {
		return errors.New("passwords do not match")