discovery.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package convey
  2. type actionSpecifier uint8
  3. const (
  4. noSpecifier actionSpecifier = iota
  5. skipConvey
  6. focusConvey
  7. )
  8. type suite struct {
  9. Situation string
  10. Test t
  11. Focus bool
  12. Func func(C) // nil means skipped
  13. FailMode FailureMode
  14. StackMode StackMode
  15. }
  16. func newSuite(situation string, failureMode FailureMode, stackMode StackMode, f func(C), test t, specifier actionSpecifier) *suite {
  17. ret := &suite{
  18. Situation: situation,
  19. Test: test,
  20. Func: f,
  21. FailMode: failureMode,
  22. StackMode: stackMode,
  23. }
  24. switch specifier {
  25. case skipConvey:
  26. ret.Func = nil
  27. case focusConvey:
  28. ret.Focus = true
  29. }
  30. return ret
  31. }
  32. func discover(items []interface{}) *suite {
  33. name, items := parseName(items)
  34. test, items := parseGoTest(items)
  35. failure, items := parseFailureMode(items)
  36. stack, items := parseStackMode(items)
  37. action, items := parseAction(items)
  38. specifier, items := parseSpecifier(items)
  39. if len(items) != 0 {
  40. conveyPanic(parseError)
  41. }
  42. return newSuite(name, failure, stack, action, test, specifier)
  43. }
  44. func item(items []interface{}) interface{} {
  45. if len(items) == 0 {
  46. conveyPanic(parseError)
  47. }
  48. return items[0]
  49. }
  50. func parseName(items []interface{}) (string, []interface{}) {
  51. if name, parsed := item(items).(string); parsed {
  52. return name, items[1:]
  53. }
  54. conveyPanic(parseError)
  55. panic("never get here")
  56. }
  57. func parseGoTest(items []interface{}) (t, []interface{}) {
  58. if test, parsed := item(items).(t); parsed {
  59. return test, items[1:]
  60. }
  61. return nil, items
  62. }
  63. func parseFailureMode(items []interface{}) (FailureMode, []interface{}) {
  64. if mode, parsed := item(items).(FailureMode); parsed {
  65. return mode, items[1:]
  66. }
  67. return FailureInherits, items
  68. }
  69. func parseStackMode(items []interface{}) (StackMode, []interface{}) {
  70. if mode, parsed := item(items).(StackMode); parsed {
  71. return mode, items[1:]
  72. }
  73. return StackInherits, items
  74. }
  75. func parseAction(items []interface{}) (func(C), []interface{}) {
  76. switch x := item(items).(type) {
  77. case nil:
  78. return nil, items[1:]
  79. case func(C):
  80. return x, items[1:]
  81. case func():
  82. return func(C) { x() }, items[1:]
  83. }
  84. conveyPanic(parseError)
  85. panic("never get here")
  86. }
  87. func parseSpecifier(items []interface{}) (actionSpecifier, []interface{}) {
  88. if len(items) == 0 {
  89. return noSpecifier, items
  90. }
  91. if spec, ok := items[0].(actionSpecifier); ok {
  92. return spec, items[1:]
  93. }
  94. conveyPanic(parseError)
  95. panic("never get here")
  96. }
  97. // This interface allows us to pass the *testing.T struct
  98. // throughout the internals of this package without ever
  99. // having to import the "testing" package.
  100. type t interface {
  101. Fail()
  102. }
  103. const parseError = "You must provide a name (string), then a *testing.T (if in outermost scope), an optional FailureMode and / or StackMode, and then an action (func())."