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.
 
 
 

171 lines
4.4 KiB

  1. // Copyright 2015 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package timeseries
  5. import (
  6. "math"
  7. "testing"
  8. "time"
  9. )
  10. func isNear(x *Float, y float64, tolerance float64) bool {
  11. return math.Abs(x.Value()-y) < tolerance
  12. }
  13. func isApproximate(x *Float, y float64) bool {
  14. return isNear(x, y, 1e-2)
  15. }
  16. func checkApproximate(t *testing.T, o Observable, y float64) {
  17. x := o.(*Float)
  18. if !isApproximate(x, y) {
  19. t.Errorf("Wanted %g, got %g", y, x.Value())
  20. }
  21. }
  22. func checkNear(t *testing.T, o Observable, y, tolerance float64) {
  23. x := o.(*Float)
  24. if !isNear(x, y, tolerance) {
  25. t.Errorf("Wanted %g +- %g, got %g", y, tolerance, x.Value())
  26. }
  27. }
  28. var baseTime = time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC)
  29. func tu(s int64) time.Time {
  30. return baseTime.Add(time.Duration(s) * time.Second)
  31. }
  32. func tu2(s int64, ns int64) time.Time {
  33. return baseTime.Add(time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond)
  34. }
  35. func TestBasicTimeSeries(t *testing.T) {
  36. ts := NewTimeSeries(NewFloat)
  37. fo := new(Float)
  38. *fo = Float(10)
  39. ts.AddWithTime(fo, tu(1))
  40. ts.AddWithTime(fo, tu(1))
  41. ts.AddWithTime(fo, tu(1))
  42. ts.AddWithTime(fo, tu(1))
  43. checkApproximate(t, ts.Range(tu(0), tu(1)), 40)
  44. checkApproximate(t, ts.Total(), 40)
  45. ts.AddWithTime(fo, tu(3))
  46. ts.AddWithTime(fo, tu(3))
  47. ts.AddWithTime(fo, tu(3))
  48. checkApproximate(t, ts.Range(tu(0), tu(2)), 40)
  49. checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
  50. checkApproximate(t, ts.Total(), 70)
  51. ts.AddWithTime(fo, tu(1))
  52. ts.AddWithTime(fo, tu(1))
  53. checkApproximate(t, ts.Range(tu(0), tu(2)), 60)
  54. checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
  55. checkApproximate(t, ts.Total(), 90)
  56. *fo = Float(100)
  57. ts.AddWithTime(fo, tu(100))
  58. checkApproximate(t, ts.Range(tu(99), tu(100)), 100)
  59. checkApproximate(t, ts.Range(tu(0), tu(4)), 36)
  60. checkApproximate(t, ts.Total(), 190)
  61. *fo = Float(10)
  62. ts.AddWithTime(fo, tu(1))
  63. ts.AddWithTime(fo, tu(1))
  64. checkApproximate(t, ts.Range(tu(0), tu(4)), 44)
  65. checkApproximate(t, ts.Range(tu(37), tu2(100, 100e6)), 100)
  66. checkApproximate(t, ts.Range(tu(50), tu2(100, 100e6)), 100)
  67. checkApproximate(t, ts.Range(tu(99), tu2(100, 100e6)), 100)
  68. checkApproximate(t, ts.Total(), 210)
  69. for i, l := range ts.ComputeRange(tu(36), tu(100), 64) {
  70. if i == 63 {
  71. checkApproximate(t, l, 100)
  72. } else {
  73. checkApproximate(t, l, 0)
  74. }
  75. }
  76. checkApproximate(t, ts.Range(tu(0), tu(100)), 210)
  77. checkApproximate(t, ts.Range(tu(10), tu(100)), 100)
  78. for i, l := range ts.ComputeRange(tu(0), tu(100), 100) {
  79. if i < 10 {
  80. checkApproximate(t, l, 11)
  81. } else if i >= 90 {
  82. checkApproximate(t, l, 10)
  83. } else {
  84. checkApproximate(t, l, 0)
  85. }
  86. }
  87. }
  88. func TestFloat(t *testing.T) {
  89. f := Float(1)
  90. if g, w := f.String(), "1"; g != w {
  91. t.Errorf("Float(1).String = %q; want %q", g, w)
  92. }
  93. f2 := Float(2)
  94. var o Observable = &f2
  95. f.Add(o)
  96. if g, w := f.Value(), 3.0; g != w {
  97. t.Errorf("Float post-add = %v; want %v", g, w)
  98. }
  99. f.Multiply(2)
  100. if g, w := f.Value(), 6.0; g != w {
  101. t.Errorf("Float post-multiply = %v; want %v", g, w)
  102. }
  103. f.Clear()
  104. if g, w := f.Value(), 0.0; g != w {
  105. t.Errorf("Float post-clear = %v; want %v", g, w)
  106. }
  107. f.CopyFrom(&f2)
  108. if g, w := f.Value(), 2.0; g != w {
  109. t.Errorf("Float post-CopyFrom = %v; want %v", g, w)
  110. }
  111. }
  112. type mockClock struct {
  113. time time.Time
  114. }
  115. func (m *mockClock) Time() time.Time { return m.time }
  116. func (m *mockClock) Set(t time.Time) { m.time = t }
  117. const buckets = 6
  118. var testResolutions = []time.Duration{
  119. 10 * time.Second, // level holds one minute of observations
  120. 100 * time.Second, // level holds ten minutes of observations
  121. 10 * time.Minute, // level holds one hour of observations
  122. }
  123. // TestTimeSeries uses a small number of buckets to force a higher
  124. // error rate on approximations from the timeseries.
  125. type TestTimeSeries struct {
  126. timeSeries
  127. }
  128. func TestExpectedErrorRate(t *testing.T) {
  129. ts := new(TestTimeSeries)
  130. fake := new(mockClock)
  131. fake.Set(time.Now())
  132. ts.timeSeries.init(testResolutions, NewFloat, buckets, fake)
  133. for i := 1; i <= 61*61; i++ {
  134. fake.Set(fake.Time().Add(1 * time.Second))
  135. ob := Float(1)
  136. ts.AddWithTime(&ob, fake.Time())
  137. // The results should be accurate within one missing bucket (1/6) of the observations recorded.
  138. checkNear(t, ts.Latest(0, buckets), min(float64(i), 60), 10)
  139. checkNear(t, ts.Latest(1, buckets), min(float64(i), 600), 100)
  140. checkNear(t, ts.Latest(2, buckets), min(float64(i), 3600), 600)
  141. }
  142. }
  143. func min(a, b float64) float64 {
  144. if a < b {
  145. return a
  146. }
  147. return b
  148. }