z_spec_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. // Copyright 2014 The Go Authors.
  2. // See https://code.google.com/p/go/source/browse/CONTRIBUTORS
  3. // Licensed under the same terms as Go itself:
  4. // https://code.google.com/p/go/source/browse/LICENSE
  5. package http2
  6. import (
  7. "encoding/xml"
  8. "flag"
  9. "fmt"
  10. "io"
  11. "os"
  12. "reflect"
  13. "regexp"
  14. "sort"
  15. "strconv"
  16. "strings"
  17. "testing"
  18. )
  19. var coverSpec = flag.Bool("coverspec", false, "Run spec coverage tests")
  20. // The global map of sentence coverage for the http2 spec.
  21. var defaultSpecCoverage specCoverage
  22. func init() {
  23. f, err := os.Open("testdata/draft-ietf-httpbis-http2.xml")
  24. if err != nil {
  25. panic(err)
  26. }
  27. defaultSpecCoverage = readSpecCov(f)
  28. }
  29. // specCover marks all sentences for section sec in defaultSpecCoverage. Sentences not
  30. // "covered" will be included in report outputed by TestSpecCoverage.
  31. func specCover(sec, sentences string) {
  32. defaultSpecCoverage.cover(sec, sentences)
  33. }
  34. type specPart struct {
  35. section string
  36. sentence string
  37. }
  38. func (ss specPart) Less(oo specPart) bool {
  39. atoi := func(s string) int {
  40. n, err := strconv.Atoi(s)
  41. if err != nil {
  42. panic(err)
  43. }
  44. return n
  45. }
  46. a := strings.Split(ss.section, ".")
  47. b := strings.Split(oo.section, ".")
  48. for i := 0; i < len(a); i++ {
  49. if i >= len(b) {
  50. return false
  51. }
  52. x, y := atoi(a[i]), atoi(b[i])
  53. if x < y {
  54. return true
  55. }
  56. }
  57. return false
  58. }
  59. type bySpecSection []specPart
  60. func (a bySpecSection) Len() int { return len(a) }
  61. func (a bySpecSection) Less(i, j int) bool { return a[i].Less(a[j]) }
  62. func (a bySpecSection) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  63. type specCoverage map[specPart]bool
  64. func readSection(sc specCoverage, d *xml.Decoder, sec []int) {
  65. sub := 0
  66. for {
  67. tk, err := d.Token()
  68. if err != nil {
  69. if err == io.EOF {
  70. return
  71. }
  72. panic(err)
  73. }
  74. switch v := tk.(type) {
  75. case xml.StartElement:
  76. if skipElement(v) {
  77. if err := d.Skip(); err != nil {
  78. panic(err)
  79. }
  80. break
  81. }
  82. if v.Name.Local == "section" {
  83. sub++
  84. readSection(sc, d, append(sec, sub))
  85. }
  86. case xml.CharData:
  87. if len(sec) == 0 {
  88. break
  89. }
  90. ssec := fmt.Sprintf("%d", sec[0])
  91. for _, n := range sec[1:] {
  92. ssec = fmt.Sprintf("%s.%d", ssec, n)
  93. }
  94. sc.addSentences(ssec, string(v))
  95. case xml.EndElement:
  96. if v.Name.Local == "section" {
  97. return
  98. }
  99. }
  100. }
  101. }
  102. var skipAnchor = map[string]bool{
  103. "intro": true,
  104. "Overview": true,
  105. }
  106. var skipTitle = map[string]bool{
  107. "Acknowledgements": true,
  108. "Change Log": true,
  109. "Document Organization": true,
  110. "Conventions and Terminology": true,
  111. }
  112. func skipElement(s xml.StartElement) bool {
  113. switch s.Name.Local {
  114. case "artwork":
  115. return true
  116. case "section":
  117. for _, attr := range s.Attr {
  118. switch attr.Name.Local {
  119. case "anchor":
  120. if skipAnchor[attr.Value] || strings.HasPrefix(attr.Value, "changes.since.") {
  121. return true
  122. }
  123. case "title":
  124. if skipTitle[attr.Value] {
  125. return true
  126. }
  127. }
  128. }
  129. }
  130. return false
  131. }
  132. func readSpecCov(r io.Reader) specCoverage {
  133. d := xml.NewDecoder(r)
  134. sc := specCoverage{}
  135. readSection(sc, d, nil)
  136. return sc
  137. }
  138. func (sc specCoverage) addSentences(sec string, sentence string) {
  139. for _, s := range parseSentences(sentence) {
  140. sc[specPart{sec, s}] = false
  141. }
  142. }
  143. func (sc specCoverage) cover(sec string, sentence string) {
  144. for _, s := range parseSentences(sentence) {
  145. p := specPart{sec, s}
  146. if _, ok := sc[p]; !ok {
  147. panic(fmt.Sprintf("Not found in spec: %q, %q", sec, s))
  148. }
  149. sc[specPart{sec, s}] = true
  150. }
  151. }
  152. var whitespaceRx = regexp.MustCompile(`\s+`)
  153. func parseSentences(sens string) []string {
  154. sens = strings.TrimSpace(sens)
  155. if sens == "" {
  156. return nil
  157. }
  158. ss := strings.Split(whitespaceRx.ReplaceAllString(sens, " "), ". ")
  159. for i, s := range ss {
  160. s = strings.TrimSpace(s)
  161. if !strings.HasSuffix(s, ".") {
  162. s += "."
  163. }
  164. ss[i] = s
  165. }
  166. return ss
  167. }
  168. func TestSpecParseSentences(t *testing.T) {
  169. tests := []struct {
  170. ss string
  171. want []string
  172. }{
  173. {"Sentence 1. Sentence 2.",
  174. []string{
  175. "Sentence 1.",
  176. "Sentence 2.",
  177. }},
  178. {"Sentence 1. \nSentence 2.\tSentence 3.",
  179. []string{
  180. "Sentence 1.",
  181. "Sentence 2.",
  182. "Sentence 3.",
  183. }},
  184. }
  185. for i, tt := range tests {
  186. got := parseSentences(tt.ss)
  187. if !reflect.DeepEqual(got, tt.want) {
  188. t.Errorf("%d: got = %q, want %q", i, got, tt.want)
  189. }
  190. }
  191. }
  192. func TestSpecBuildCoverageTable(t *testing.T) {
  193. testdata := `
  194. <rfc>
  195. <middle>
  196. <section anchor="foo" title="Introduction">
  197. <t>Foo.</t>
  198. <t><t>Sentence 1.
  199. Sentence 2
  200. . Sentence 3.</t></t>
  201. </section>
  202. <section anchor="bar" title="Introduction">
  203. <t>Bar.</t>
  204. <section anchor="bar" title="Introduction">
  205. <t>Baz.</t>
  206. </section>
  207. </section>
  208. </middle>
  209. </rfc>`
  210. got := readSpecCov(strings.NewReader(testdata))
  211. want := specCoverage{
  212. specPart{"1", "Foo."}: false,
  213. specPart{"1", "Sentence 1."}: false,
  214. specPart{"1", "Sentence 2."}: false,
  215. specPart{"1", "Sentence 3."}: false,
  216. specPart{"2", "Bar."}: false,
  217. specPart{"2.1", "Baz."}: false,
  218. }
  219. if !reflect.DeepEqual(got, want) {
  220. t.Errorf("got = %+v, want %+v", got, want)
  221. }
  222. }
  223. func TestSpecUncovered(t *testing.T) {
  224. testdata := `
  225. <rfc>
  226. <middle>
  227. <section anchor="foo" title="Introduction">
  228. <t>Foo.</t>
  229. <t><t>Sentence 1.</t></t>
  230. </section>
  231. </middle>
  232. </rfc>`
  233. sp := readSpecCov(strings.NewReader(testdata))
  234. sp.cover("1", "Foo. Sentence 1.")
  235. want := specCoverage{
  236. specPart{"1", "Foo."}: true,
  237. specPart{"1", "Sentence 1."}: true,
  238. }
  239. if !reflect.DeepEqual(sp, want) {
  240. t.Errorf("got = %+v, want %+v", sp, want)
  241. }
  242. defer func() {
  243. if err := recover(); err == nil {
  244. t.Error("expected panic")
  245. }
  246. }()
  247. sp.cover("1", "Not in spec.")
  248. }
  249. func TestSpecCoverage(t *testing.T) {
  250. if !*coverSpec {
  251. t.Skip("skipping spec coverage without -coverspec")
  252. }
  253. var notCovered bySpecSection
  254. for p, covered := range defaultSpecCoverage {
  255. if !covered {
  256. notCovered = append(notCovered, p)
  257. }
  258. }
  259. if len(notCovered) == 0 {
  260. return
  261. }
  262. sort.Sort(notCovered)
  263. const shortLen = 5
  264. if testing.Short() && len(notCovered) > shortLen {
  265. notCovered = notCovered[:shortLen]
  266. }
  267. t.Logf("COVER REPORT:")
  268. fails := 0
  269. for _, p := range notCovered {
  270. t.Errorf("\tSECTION %s: %s", p.section, p.sentence)
  271. fails++
  272. }
  273. t.Logf("%d sections not covered", fails)
  274. }