package irctest import ( "bufio" "net" "strings" "sync" "time" ) // An Interaction is a "simulated" server that will trigger the // client. type Interaction struct { wg sync.WaitGroup Strict bool Lines []InteractionLine Log []string Failure *InteractionFailure } // Listen listens for a client in a separate goroutine. func (interaction *Interaction) Listen() (addr string, err error) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return "", err } lines := make([]InteractionLine, len(interaction.Lines)) copy(lines, interaction.Lines) go func() { interaction.wg.Add(1) defer interaction.wg.Done() conn, err := listener.Accept() if err != nil { interaction.Failure = &InteractionFailure{ Index: -1, NetErr: err, } return } defer conn.Close() reader := bufio.NewReader(conn) for i := 0; i < len(lines); i++ { line := lines[i] if line.Server != "" { _ = conn.SetWriteDeadline(time.Now().Add(time.Second * 2)) _, err := conn.Write(append([]byte(line.Server), '\r', '\n')) if err != nil { interaction.Failure = &InteractionFailure{ Index: i, NetErr: err, } return } } else if line.Client != "" { _ = conn.SetReadDeadline(time.Now().Add(time.Second * 2)) input, err := reader.ReadString('\n') if err != nil { interaction.Failure = &InteractionFailure{ Index: i, NetErr: err, } return } input = strings.Replace(input, "\r", "", -1) input = strings.Replace(input, "\n", "", 1) match := line.Client success := false if strings.HasSuffix(match, "*") { success = strings.HasPrefix(input, match[:len(match)-1]) } else { success = match == input } interaction.Log = append(interaction.Log, input) if !success { if !interaction.Strict { i-- continue } interaction.Failure = &InteractionFailure{ Index: i, Result: input, } return } } else if line.Callback != nil { err := line.Callback() if err != nil { interaction.Failure = &InteractionFailure{ Index: i, CBErr: err, } return } } } }() return listener.Addr().String(), nil } // Wait waits for the setup to be done. It's safe to check // Failure after that. func (interaction *Interaction) Wait() { interaction.wg.Wait() } // InteractionFailure signifies a test failure. type InteractionFailure struct { Index int Result string NetErr error CBErr error } // InteractionLine is part of an interaction, whether it is a line // that is sent to a client or a line expected from a client. type InteractionLine struct { Client string Server string Callback func() error }