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.

138 lines
2.7 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. if line.Data != "" {
  41. switch line.Kind {
  42. case 'S':
  43. {
  44. _, err := conn.Write(append([]byte(line.Data), '\r', '\n'))
  45. if err != nil {
  46. interaction.Failure = &InteractionFailure{
  47. Index: i, NetErr: err,
  48. }
  49. return
  50. }
  51. }
  52. case 'C':
  53. {
  54. conn.SetReadDeadline(time.Now().Add(time.Second * 2))
  55. input, err := reader.ReadString('\n')
  56. if err != nil {
  57. interaction.Failure = &InteractionFailure{
  58. Index: i, NetErr: err,
  59. }
  60. return
  61. }
  62. input = strings.Replace(input, "\r", "", -1)
  63. input = strings.Replace(input, "\n", "", 1)
  64. match := line.Data
  65. success := false
  66. if strings.HasSuffix(match, "*") {
  67. success = strings.HasPrefix(input, match[:len(match)-1])
  68. } else {
  69. success = match == input
  70. }
  71. interaction.Log = append(interaction.Log, input)
  72. if !success {
  73. if !interaction.Strict {
  74. i--
  75. continue
  76. }
  77. interaction.Failure = &InteractionFailure{
  78. Index: i, Result: input,
  79. }
  80. return
  81. }
  82. }
  83. }
  84. }
  85. if line.Callback != nil {
  86. err := line.Callback()
  87. if err != nil {
  88. interaction.Failure = &InteractionFailure{
  89. Index: i, CBErr: err,
  90. }
  91. return
  92. }
  93. }
  94. }
  95. }()
  96. return listener.Addr().String(), nil
  97. }
  98. // Wait waits for the setup to be done. It's safe to check
  99. // Failure after that.
  100. func (interaction *Interaction) Wait() {
  101. interaction.wg.Wait()
  102. }
  103. // InteractionFailure signifies a test failure.
  104. type InteractionFailure struct {
  105. Index int
  106. Result string
  107. NetErr error
  108. CBErr error
  109. }
  110. // InteractionLine is part of an interaction, whether it is a line
  111. // that is sent to a client or a line expected from a client.
  112. type InteractionLine struct {
  113. Kind byte
  114. Data string
  115. Callback func() error
  116. }