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.
 
 
 

493 lines
12 KiB

  1. // Copyright 2017, OpenCensus Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. package view
  16. import (
  17. "context"
  18. "testing"
  19. "github.com/google/go-cmp/cmp"
  20. "go.opencensus.io/exemplar"
  21. "go.opencensus.io/stats"
  22. "go.opencensus.io/tag"
  23. )
  24. func Test_View_MeasureFloat64_AggregationDistribution(t *testing.T) {
  25. k1, _ := tag.NewKey("k1")
  26. k2, _ := tag.NewKey("k2")
  27. k3, _ := tag.NewKey("k3")
  28. agg1 := Distribution(2)
  29. m := stats.Int64("Test_View_MeasureFloat64_AggregationDistribution/m1", "", stats.UnitDimensionless)
  30. view1 := &View{
  31. TagKeys: []tag.Key{k1, k2},
  32. Measure: m,
  33. Aggregation: agg1,
  34. }
  35. view, err := newViewInternal(view1)
  36. if err != nil {
  37. t.Fatal(err)
  38. }
  39. type tagString struct {
  40. k tag.Key
  41. v string
  42. }
  43. type record struct {
  44. f float64
  45. tags []tagString
  46. }
  47. type testCase struct {
  48. label string
  49. records []record
  50. wantRows []*Row
  51. }
  52. tcs := []testCase{
  53. {
  54. "1",
  55. []record{
  56. {1, []tagString{{k1, "v1"}}},
  57. {5, []tagString{{k1, "v1"}}},
  58. },
  59. []*Row{
  60. {
  61. []tag.Tag{{Key: k1, Value: "v1"}},
  62. &DistributionData{
  63. Count: 2, Min: 1, Max: 5, Mean: 3, SumOfSquaredDev: 8, CountPerBucket: []int64{1, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  64. },
  65. },
  66. },
  67. },
  68. {
  69. "2",
  70. []record{
  71. {1, []tagString{{k1, "v1"}}},
  72. {5, []tagString{{k2, "v2"}}},
  73. },
  74. []*Row{
  75. {
  76. []tag.Tag{{Key: k1, Value: "v1"}},
  77. &DistributionData{
  78. Count: 1, Min: 1, Max: 1, Mean: 1, CountPerBucket: []int64{1, 0}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  79. },
  80. },
  81. {
  82. []tag.Tag{{Key: k2, Value: "v2"}},
  83. &DistributionData{
  84. Count: 1, Min: 5, Max: 5, Mean: 5, CountPerBucket: []int64{0, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  85. },
  86. },
  87. },
  88. },
  89. {
  90. "3",
  91. []record{
  92. {1, []tagString{{k1, "v1"}}},
  93. {5, []tagString{{k1, "v1"}, {k3, "v3"}}},
  94. {1, []tagString{{k1, "v1 other"}}},
  95. {5, []tagString{{k2, "v2"}}},
  96. {5, []tagString{{k1, "v1"}, {k2, "v2"}}},
  97. },
  98. []*Row{
  99. {
  100. []tag.Tag{{Key: k1, Value: "v1"}},
  101. &DistributionData{
  102. Count: 2, Min: 1, Max: 5, Mean: 3, SumOfSquaredDev: 8, CountPerBucket: []int64{1, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  103. },
  104. },
  105. {
  106. []tag.Tag{{Key: k1, Value: "v1 other"}},
  107. &DistributionData{
  108. Count: 1, Min: 1, Max: 1, Mean: 1, CountPerBucket: []int64{1, 0}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  109. },
  110. },
  111. {
  112. []tag.Tag{{Key: k2, Value: "v2"}},
  113. &DistributionData{
  114. Count: 1, Min: 5, Max: 5, Mean: 5, CountPerBucket: []int64{0, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  115. },
  116. },
  117. {
  118. []tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
  119. &DistributionData{
  120. Count: 1, Min: 5, Max: 5, Mean: 5, CountPerBucket: []int64{0, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  121. },
  122. },
  123. },
  124. },
  125. {
  126. "4",
  127. []record{
  128. {1, []tagString{{k1, "v1 is a very long value key"}}},
  129. {5, []tagString{{k1, "v1 is a very long value key"}, {k3, "v3"}}},
  130. {1, []tagString{{k1, "v1 is another very long value key"}}},
  131. {1, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
  132. {5, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
  133. {3, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
  134. {3, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
  135. },
  136. []*Row{
  137. {
  138. []tag.Tag{{Key: k1, Value: "v1 is a very long value key"}},
  139. &DistributionData{
  140. Count: 2, Min: 1, Max: 5, Mean: 3, SumOfSquaredDev: 8, CountPerBucket: []int64{1, 1}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  141. },
  142. },
  143. {
  144. []tag.Tag{{Key: k1, Value: "v1 is another very long value key"}},
  145. &DistributionData{
  146. Count: 1, Min: 1, Max: 1, Mean: 1, CountPerBucket: []int64{1, 0}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  147. },
  148. },
  149. {
  150. []tag.Tag{{Key: k1, Value: "v1 is a very long value key"}, {Key: k2, Value: "v2 is a very long value key"}},
  151. &DistributionData{
  152. Count: 4, Min: 1, Max: 5, Mean: 3, SumOfSquaredDev: 2.66666666666667 * 3, CountPerBucket: []int64{1, 3}, bounds: []float64{2}, ExemplarsPerBucket: []*exemplar.Exemplar{nil, nil},
  153. },
  154. },
  155. },
  156. },
  157. }
  158. for _, tc := range tcs {
  159. view.clearRows()
  160. view.subscribe()
  161. for _, r := range tc.records {
  162. mods := []tag.Mutator{}
  163. for _, t := range r.tags {
  164. mods = append(mods, tag.Insert(t.k, t.v))
  165. }
  166. ctx, err := tag.New(context.Background(), mods...)
  167. if err != nil {
  168. t.Errorf("%v: New = %v", tc.label, err)
  169. }
  170. e := &exemplar.Exemplar{
  171. Value: r.f,
  172. Attachments: exemplar.AttachmentsFromContext(ctx),
  173. }
  174. view.addSample(tag.FromContext(ctx), e)
  175. }
  176. gotRows := view.collectedRows()
  177. for i, got := range gotRows {
  178. if !containsRow(tc.wantRows, got) {
  179. t.Errorf("%v-%d: got row %v; want none", tc.label, i, got)
  180. break
  181. }
  182. }
  183. for i, want := range tc.wantRows {
  184. if !containsRow(gotRows, want) {
  185. t.Errorf("%v-%d: got none; want row %v", tc.label, i, want)
  186. break
  187. }
  188. }
  189. }
  190. }
  191. func Test_View_MeasureFloat64_AggregationSum(t *testing.T) {
  192. k1, _ := tag.NewKey("k1")
  193. k2, _ := tag.NewKey("k2")
  194. k3, _ := tag.NewKey("k3")
  195. m := stats.Int64("Test_View_MeasureFloat64_AggregationSum/m1", "", stats.UnitDimensionless)
  196. view, err := newViewInternal(&View{TagKeys: []tag.Key{k1, k2}, Measure: m, Aggregation: Sum()})
  197. if err != nil {
  198. t.Fatal(err)
  199. }
  200. type tagString struct {
  201. k tag.Key
  202. v string
  203. }
  204. type record struct {
  205. f float64
  206. tags []tagString
  207. }
  208. tcs := []struct {
  209. label string
  210. records []record
  211. wantRows []*Row
  212. }{
  213. {
  214. "1",
  215. []record{
  216. {1, []tagString{{k1, "v1"}}},
  217. {5, []tagString{{k1, "v1"}}},
  218. },
  219. []*Row{
  220. {
  221. []tag.Tag{{Key: k1, Value: "v1"}},
  222. &SumData{Value: 6},
  223. },
  224. },
  225. },
  226. {
  227. "2",
  228. []record{
  229. {1, []tagString{{k1, "v1"}}},
  230. {5, []tagString{{k2, "v2"}}},
  231. },
  232. []*Row{
  233. {
  234. []tag.Tag{{Key: k1, Value: "v1"}},
  235. &SumData{Value: 1},
  236. },
  237. {
  238. []tag.Tag{{Key: k2, Value: "v2"}},
  239. &SumData{Value: 5},
  240. },
  241. },
  242. },
  243. {
  244. "3",
  245. []record{
  246. {1, []tagString{{k1, "v1"}}},
  247. {5, []tagString{{k1, "v1"}, {k3, "v3"}}},
  248. {1, []tagString{{k1, "v1 other"}}},
  249. {5, []tagString{{k2, "v2"}}},
  250. {5, []tagString{{k1, "v1"}, {k2, "v2"}}},
  251. },
  252. []*Row{
  253. {
  254. []tag.Tag{{Key: k1, Value: "v1"}},
  255. &SumData{Value: 6},
  256. },
  257. {
  258. []tag.Tag{{Key: k1, Value: "v1 other"}},
  259. &SumData{Value: 1},
  260. },
  261. {
  262. []tag.Tag{{Key: k2, Value: "v2"}},
  263. &SumData{Value: 5},
  264. },
  265. {
  266. []tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
  267. &SumData{Value: 5},
  268. },
  269. },
  270. },
  271. }
  272. for _, tt := range tcs {
  273. view.clearRows()
  274. view.subscribe()
  275. for _, r := range tt.records {
  276. mods := []tag.Mutator{}
  277. for _, t := range r.tags {
  278. mods = append(mods, tag.Insert(t.k, t.v))
  279. }
  280. ctx, err := tag.New(context.Background(), mods...)
  281. if err != nil {
  282. t.Errorf("%v: New = %v", tt.label, err)
  283. }
  284. e := &exemplar.Exemplar{
  285. Value: r.f,
  286. }
  287. view.addSample(tag.FromContext(ctx), e)
  288. }
  289. gotRows := view.collectedRows()
  290. for i, got := range gotRows {
  291. if !containsRow(tt.wantRows, got) {
  292. t.Errorf("%v-%d: got row %v; want none", tt.label, i, got)
  293. break
  294. }
  295. }
  296. for i, want := range tt.wantRows {
  297. if !containsRow(gotRows, want) {
  298. t.Errorf("%v-%d: got none; want row %v", tt.label, i, want)
  299. break
  300. }
  301. }
  302. }
  303. }
  304. func TestCanonicalize(t *testing.T) {
  305. k1, _ := tag.NewKey("k1")
  306. k2, _ := tag.NewKey("k2")
  307. m := stats.Int64("TestCanonicalize/m1", "desc desc", stats.UnitDimensionless)
  308. v := &View{TagKeys: []tag.Key{k2, k1}, Measure: m, Aggregation: Sum()}
  309. err := v.canonicalize()
  310. if err != nil {
  311. t.Fatal(err)
  312. }
  313. if got, want := v.Name, "TestCanonicalize/m1"; got != want {
  314. t.Errorf("vc.Name = %q; want %q", got, want)
  315. }
  316. if got, want := v.Description, "desc desc"; got != want {
  317. t.Errorf("vc.Description = %q; want %q", got, want)
  318. }
  319. if got, want := len(v.TagKeys), 2; got != want {
  320. t.Errorf("len(vc.TagKeys) = %d; want %d", got, want)
  321. }
  322. if got, want := v.TagKeys[0].Name(), "k1"; got != want {
  323. t.Errorf("vc.TagKeys[0].Name() = %q; want %q", got, want)
  324. }
  325. }
  326. func TestViewSortedKeys(t *testing.T) {
  327. k1, _ := tag.NewKey("a")
  328. k2, _ := tag.NewKey("b")
  329. k3, _ := tag.NewKey("c")
  330. ks := []tag.Key{k1, k3, k2}
  331. m := stats.Int64("TestViewSortedKeys/m1", "", stats.UnitDimensionless)
  332. Register(&View{
  333. Name: "sort_keys",
  334. Description: "desc sort_keys",
  335. TagKeys: ks,
  336. Measure: m,
  337. Aggregation: Sum(),
  338. })
  339. // Register normalizes the view by sorting the tag keys, retrieve the normalized view
  340. v := Find("sort_keys")
  341. want := []string{"a", "b", "c"}
  342. vks := v.TagKeys
  343. if len(vks) != len(want) {
  344. t.Errorf("Keys = %+v; want %+v", vks, want)
  345. }
  346. for i, v := range want {
  347. if got, want := v, vks[i].Name(); got != want {
  348. t.Errorf("View name = %q; want %q", got, want)
  349. }
  350. }
  351. }
  352. // containsRow returns true if rows contain r.
  353. func containsRow(rows []*Row, r *Row) bool {
  354. for _, x := range rows {
  355. if r.Equal(x) {
  356. return true
  357. }
  358. }
  359. return false
  360. }
  361. func TestRegisterUnregisterParity(t *testing.T) {
  362. measures := []stats.Measure{
  363. stats.Int64("ifoo", "iFOO", "iBar"),
  364. stats.Float64("ffoo", "fFOO", "fBar"),
  365. }
  366. aggregations := []*Aggregation{
  367. Count(),
  368. Sum(),
  369. Distribution(1, 2.0, 4.0, 8.0, 16.0),
  370. }
  371. for i := 0; i < 10; i++ {
  372. for _, m := range measures {
  373. for _, agg := range aggregations {
  374. v := &View{
  375. Aggregation: agg,
  376. Name: "Lookup here",
  377. Measure: m,
  378. }
  379. if err := Register(v); err != nil {
  380. t.Errorf("Iteration #%d:\nMeasure: (%#v)\nAggregation (%#v)\nError: %v", i, m, agg, err)
  381. }
  382. Unregister(v)
  383. }
  384. }
  385. }
  386. }
  387. func TestRegisterAfterMeasurement(t *testing.T) {
  388. // Tests that we can register views after measurements are created and
  389. // they still take effect.
  390. m := stats.Int64(t.Name(), "", stats.UnitDimensionless)
  391. mm := m.M(1)
  392. ctx := context.Background()
  393. stats.Record(ctx, mm)
  394. v := &View{
  395. Measure: m,
  396. Aggregation: Count(),
  397. }
  398. if err := Register(v); err != nil {
  399. t.Fatal(err)
  400. }
  401. rows, err := RetrieveData(v.Name)
  402. if err != nil {
  403. t.Fatal(err)
  404. }
  405. if len(rows) > 0 {
  406. t.Error("View should not have data")
  407. }
  408. stats.Record(ctx, mm)
  409. rows, err = RetrieveData(v.Name)
  410. if err != nil {
  411. t.Fatal(err)
  412. }
  413. if len(rows) == 0 {
  414. t.Error("View should have data")
  415. }
  416. }
  417. func TestViewRegister_negativeBucketBounds(t *testing.T) {
  418. m := stats.Int64("TestViewRegister_negativeBucketBounds", "", "")
  419. v := &View{
  420. Measure: m,
  421. Aggregation: Distribution(-1, 2),
  422. }
  423. err := Register(v)
  424. if err != ErrNegativeBucketBounds {
  425. t.Errorf("Expected ErrNegativeBucketBounds, got %v", err)
  426. }
  427. }
  428. func TestViewRegister_sortBuckets(t *testing.T) {
  429. m := stats.Int64("TestViewRegister_sortBuckets", "", "")
  430. v := &View{
  431. Measure: m,
  432. Aggregation: Distribution(2, 1),
  433. }
  434. err := Register(v)
  435. if err != nil {
  436. t.Fatalf("Unexpected err %s", err)
  437. }
  438. want := []float64{1, 2}
  439. if diff := cmp.Diff(v.Aggregation.Buckets, want); diff != "" {
  440. t.Errorf("buckets differ -got +want: %s", diff)
  441. }
  442. }
  443. func TestViewRegister_dropZeroBuckets(t *testing.T) {
  444. m := stats.Int64("TestViewRegister_dropZeroBuckets", "", "")
  445. v := &View{
  446. Measure: m,
  447. Aggregation: Distribution(2, 0, 1),
  448. }
  449. err := Register(v)
  450. if err != nil {
  451. t.Fatalf("Unexpected err %s", err)
  452. }
  453. want := []float64{1, 2}
  454. if diff := cmp.Diff(v.Aggregation.Buckets, want); diff != "" {
  455. t.Errorf("buckets differ -got +want: %s", diff)
  456. }
  457. }