Mirror of github.com/gissleh/irc
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

124 lines
2.4 KiB

  1. package irctest
  2. import (
  3. "bufio"
  4. "net"
  5. "strings"
  6. "sync"
  7. "time"
  8. )
  9. // An Interaction is a "simulated" server that will trigger the
  10. // client.
  11. type Interaction struct {
  12. wg sync.WaitGroup
  13. Strict bool
  14. Lines []InteractionLine
  15. Log []string
  16. Failure *InteractionFailure
  17. }
  18. // Listen listens for a client in a separate goroutine.
  19. func (interaction *Interaction) Listen() (addr string, err error) {
  20. listener, err := net.Listen("tcp", "127.0.0.1:0")
  21. if err != nil {
  22. return "", err
  23. }
  24. lines := make([]InteractionLine, len(interaction.Lines))
  25. copy(lines, interaction.Lines)
  26. go func() {
  27. interaction.wg.Add(1)
  28. defer interaction.wg.Done()
  29. conn, err := listener.Accept()
  30. if err != nil {
  31. interaction.Failure = &InteractionFailure{
  32. Index: -1, NetErr: err,
  33. }
  34. return
  35. }
  36. defer conn.Close()
  37. reader := bufio.NewReader(conn)
  38. for i := 0; i < len(lines); i++ {
  39. line := lines[i]
  40. switch line.Kind {
  41. case 'S':
  42. {
  43. _, err := conn.Write(append([]byte(line.Data), '\r', '\n'))
  44. if err != nil {
  45. interaction.Failure = &InteractionFailure{
  46. Index: i, NetErr: err,
  47. }
  48. return
  49. }
  50. }
  51. case 'C':
  52. {
  53. conn.SetReadDeadline(time.Now().Add(time.Second))
  54. input, err := reader.ReadString('\n')
  55. if err != nil {
  56. interaction.Failure = &InteractionFailure{
  57. Index: i, NetErr: err,
  58. }
  59. return
  60. }
  61. input = strings.Replace(input, "\r", "", -1)
  62. input = strings.Replace(input, "\n", "", 1)
  63. match := line.Data
  64. success := false
  65. if strings.HasSuffix(match, "*") {
  66. success = strings.HasPrefix(input, match[:len(match)-1])
  67. } else {
  68. success = match == input
  69. }
  70. interaction.Log = append(interaction.Log, input)
  71. if !success {
  72. if !interaction.Strict {
  73. i--
  74. continue
  75. }
  76. interaction.Failure = &InteractionFailure{
  77. Index: i, Result: input,
  78. }
  79. return
  80. }
  81. }
  82. }
  83. }
  84. }()
  85. return listener.Addr().String(), nil
  86. }
  87. // Wait waits for the setup to be done. It's safe to check
  88. // Failure after that.
  89. func (interaction *Interaction) Wait() {
  90. interaction.wg.Wait()
  91. }
  92. // InteractionFailure signifies a test failure.
  93. type InteractionFailure struct {
  94. Index int
  95. Result string
  96. NetErr error
  97. }
  98. // InteractionLine is part of an interaction, whether it is a line
  99. // that is sent to a client or a line expected from a client.
  100. type InteractionLine struct {
  101. Kind byte
  102. Data string
  103. }