path_test.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright (c) 2017 Arista Networks, Inc.
  2. // Use of this source code is governed by the Apache License 2.0
  3. // that can be found in the COPYING file.
  4. package gnmi
  5. import (
  6. "fmt"
  7. "testing"
  8. "notabug.org/themusicgod1/goarista/test"
  9. pb "github.com/openconfig/gnmi/proto/gnmi"
  10. )
  11. func p(s ...string) []string {
  12. return s
  13. }
  14. func TestSplitPath(t *testing.T) {
  15. for i, tc := range []struct {
  16. in string
  17. exp []string
  18. }{{
  19. in: "/foo/bar",
  20. exp: p("foo", "bar"),
  21. }, {
  22. in: "/foo/bar/",
  23. exp: p("foo", "bar"),
  24. }, {
  25. in: "//foo//bar//",
  26. exp: p("", "foo", "", "bar", ""),
  27. }, {
  28. in: "/foo[name=///]/bar",
  29. exp: p("foo[name=///]", "bar"),
  30. }, {
  31. in: `/foo[name=[\\\]/]/bar`,
  32. exp: p(`foo[name=[\\\]/]`, "bar"),
  33. }, {
  34. in: `/foo[name=[\\]/bar`,
  35. exp: p(`foo[name=[\\]`, "bar"),
  36. }, {
  37. in: "/foo[a=1][b=2]/bar",
  38. exp: p("foo[a=1][b=2]", "bar"),
  39. }, {
  40. in: "/foo[a=1\\]2][b=2]/bar",
  41. exp: p("foo[a=1\\]2][b=2]", "bar"),
  42. }, {
  43. in: "/foo[a=1][b=2]/bar\\baz",
  44. exp: p("foo[a=1][b=2]", "bar\\baz"),
  45. }} {
  46. got := SplitPath(tc.in)
  47. if !test.DeepEqual(tc.exp, got) {
  48. t.Errorf("[%d] unexpect split for %q. Expected: %v, Got: %v",
  49. i, tc.in, tc.exp, got)
  50. }
  51. }
  52. }
  53. func TestStrPath(t *testing.T) {
  54. for i, tc := range []struct {
  55. path string
  56. }{{
  57. path: "/",
  58. }, {
  59. path: "/foo/bar",
  60. }, {
  61. path: "/foo[name=a]/bar",
  62. }, {
  63. path: "/foo[a=1][b=2]/bar",
  64. }, {
  65. path: "/foo[a=1\\]2][b=2]/bar",
  66. }, {
  67. path: "/foo[a=1][b=2]/bar\\/baz",
  68. }} {
  69. sElms := SplitPath(tc.path)
  70. pbPath, err := ParseGNMIElements(sElms)
  71. if err != nil {
  72. t.Errorf("failed to parse %s: %s", sElms, err)
  73. }
  74. s := StrPath(pbPath)
  75. if !test.DeepEqual(tc.path, s) {
  76. t.Errorf("[%d] want %s, got %s", i, tc.path, s)
  77. }
  78. }
  79. }
  80. func TestOriginCLIPath(t *testing.T) {
  81. path := "cli"
  82. sElms := SplitPath(path)
  83. pbPath, err := ParseGNMIElements(sElms)
  84. if err != nil {
  85. t.Fatal(err)
  86. }
  87. expected := pb.Path{Origin: "cli"}
  88. if !test.DeepEqual(expected, *pbPath) {
  89. t.Errorf("want %v, got %v", expected, *pbPath)
  90. }
  91. }
  92. func TestStrPathBackwardsCompat(t *testing.T) {
  93. for i, tc := range []struct {
  94. path *pb.Path
  95. str string
  96. }{{
  97. path: &pb.Path{
  98. Element: p("foo[a=1][b=2]", "bar"),
  99. },
  100. str: "/foo[a=1][b=2]/bar",
  101. }} {
  102. got := StrPath(tc.path)
  103. if got != tc.str {
  104. t.Errorf("[%d] want %q, got %q", i, tc.str, got)
  105. }
  106. }
  107. }
  108. func TestParseElement(t *testing.T) {
  109. // test cases
  110. cases := []struct {
  111. // name is the name of the test useful if you want to run a single test
  112. // from the command line -run TestParseElement/<name>
  113. name string
  114. // in is the path element to be parsed
  115. in string
  116. // fieldName is field name (YANG node name) expected to be parsed from the path element.
  117. // Normally this is simply the path element, or if the path element contains keys this is
  118. // the text before the first [
  119. fieldName string
  120. // keys is a map of the expected key value pairs from within the []s in the
  121. // `path element.
  122. //
  123. // For example prefix[ip-prefix=10.0.0.0/24][masklength-range=26..28]
  124. // fieldName would be "prefix"
  125. // keys would be {"ip-prefix": "10.0.0.0/24", "masklength-range": "26..28"}
  126. keys map[string]string
  127. // expectedError is the exact error we expect.
  128. expectedError error
  129. }{{
  130. name: "no_elms",
  131. in: "hello",
  132. fieldName: "hello",
  133. }, {
  134. name: "single_open",
  135. in: "[",
  136. expectedError: fmt.Errorf("failed to find element name in %q", "["),
  137. }, {
  138. name: "no_equal_no_close",
  139. in: "hello[there",
  140. expectedError: fmt.Errorf("failed to find '=' in %q", "[there"),
  141. }, {
  142. name: "no_equals",
  143. in: "hello[there]",
  144. expectedError: fmt.Errorf("failed to find '=' in %q", "[there]"),
  145. }, {
  146. name: "no_left_side",
  147. in: "hello[=there]",
  148. expectedError: fmt.Errorf("failed to find key name in %q", "[=there]"),
  149. }, {
  150. name: "no_right_side",
  151. in: "hello[there=]",
  152. expectedError: fmt.Errorf("failed to find key value in %q", "[there=]"),
  153. }, {
  154. name: "hanging_escape",
  155. in: "hello[there\\",
  156. expectedError: fmt.Errorf("failed to find '=' in %q", "[there\\"),
  157. }, {
  158. name: "single_name_value",
  159. in: "hello[there=where]",
  160. fieldName: "hello",
  161. keys: map[string]string{"there": "where"},
  162. }, {
  163. name: "single_value_with=",
  164. in: "hello[there=whe=r=e]",
  165. fieldName: "hello",
  166. keys: map[string]string{"there": "whe=r=e"},
  167. }, {
  168. name: "single_value_with=_and_escaped_]",
  169. in: `hello[there=whe=\]r=e]`,
  170. fieldName: "hello",
  171. keys: map[string]string{"there": `whe=]r=e`},
  172. }, {
  173. name: "single_value_with[",
  174. in: "hello[there=w[[here]",
  175. fieldName: "hello",
  176. keys: map[string]string{"there": "w[[here"},
  177. }, {
  178. name: "value_single_open",
  179. in: "hello[first=value][",
  180. expectedError: fmt.Errorf("failed to find '=' in %q", "["),
  181. }, {
  182. name: "value_no_close",
  183. in: "hello[there=where][somename",
  184. expectedError: fmt.Errorf("failed to find '=' in %q", "[somename"),
  185. }, {
  186. name: "value_no_equals",
  187. in: "hello[there=where][somename]",
  188. expectedError: fmt.Errorf("failed to find '=' in %q", "[somename]"),
  189. }, {
  190. name: "no_left_side",
  191. in: "hello[there=where][=somevalue]",
  192. expectedError: fmt.Errorf("failed to find key name in %q", "[=somevalue]"),
  193. }, {
  194. name: "no_right_side",
  195. in: "hello[there=where][somename=]",
  196. expectedError: fmt.Errorf("failed to find key value in %q", "[somename=]"),
  197. }, {
  198. name: "two_name_values",
  199. in: "hello[there=where][somename=somevalue]",
  200. fieldName: "hello",
  201. keys: map[string]string{"there": "where", "somename": "somevalue"},
  202. }, {
  203. name: "three_name_values",
  204. in: "hello[there=where][somename=somevalue][anothername=value]",
  205. fieldName: "hello",
  206. keys: map[string]string{"there": "where", "somename": "somevalue",
  207. "anothername": "value"},
  208. }, {
  209. name: "aserisk_value",
  210. in: "hello[there=*][somename=somevalue][anothername=value]",
  211. fieldName: "hello",
  212. keys: map[string]string{"there": "*", "somename": "somevalue",
  213. "anothername": "value"},
  214. }}
  215. for _, tc := range cases {
  216. t.Run(tc.name, func(t *testing.T) {
  217. fieldName, keys, err := parseElement(tc.in)
  218. if !test.DeepEqual(tc.expectedError, err) {
  219. t.Fatalf("[%s] expected err %#v, got %#v", tc.name, tc.expectedError, err)
  220. }
  221. if !test.DeepEqual(tc.keys, keys) {
  222. t.Fatalf("[%s] expected output %#v, got %#v", tc.name, tc.keys, keys)
  223. }
  224. if tc.fieldName != fieldName {
  225. t.Fatalf("[%s] expected field name %s, got %s", tc.name, tc.fieldName, fieldName)
  226. }
  227. })
  228. }
  229. }
  230. func BenchmarkPathElementToSigleElementName(b *testing.B) {
  231. for i := 0; i < b.N; i++ {
  232. _, _, _ = parseElement("hello")
  233. }
  234. }
  235. func BenchmarkPathElementTwoKeys(b *testing.B) {
  236. for i := 0; i < b.N; i++ {
  237. _, _, _ = parseElement("hello[hello=world][bye=moon]")
  238. }
  239. }
  240. func BenchmarkPathElementBadKeys(b *testing.B) {
  241. for i := 0; i < b.N; i++ {
  242. _, _, _ = parseElement("hello[hello=world][byemoon]")
  243. }
  244. }
  245. func BenchmarkPathElementMaxKeys(b *testing.B) {
  246. for i := 0; i < b.N; i++ {
  247. _, _, _ = parseElement("hello[name=firstName][name=secondName][name=thirdName]" +
  248. "[name=fourthName][name=fifthName][name=sixthName]")
  249. }
  250. }