github.com/go-graphite/carbonapi@v0.17.0/tests/compare/compare.go (about)

     1  package compare
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/go-graphite/carbonapi/expr/types"
    10  )
    11  
    12  func compareFloat64(v1, v2 float64) bool {
    13  	if math.IsNaN(v1) && math.IsNaN(v2) {
    14  		return true
    15  	}
    16  	if math.IsInf(v1, 1) && math.IsInf(v2, 1) {
    17  		return true
    18  	}
    19  
    20  	if math.IsInf(v1, 0) && math.IsInf(v2, 0) {
    21  		return true
    22  	}
    23  
    24  	d := math.Abs(v1 - v2)
    25  	return d < eps
    26  }
    27  
    28  func deepCompareFields(v1, v2 reflect.Value) bool {
    29  	if !v1.CanInterface() {
    30  		return true
    31  	}
    32  	t1 := v1.Type()
    33  	if t1.Comparable() {
    34  		if t1.Name() == "float64" {
    35  			return compareFloat64(v1.Interface().(float64), v2.Interface().(float64))
    36  		}
    37  		if t1.Name() == "float32" {
    38  			v1f64 := float64(v1.Interface().(float32))
    39  			v2f64 := float64(v2.Interface().(float32))
    40  			return compareFloat64(v1f64, v2f64)
    41  		}
    42  		return reflect.DeepEqual(v1.Interface(), v2.Interface())
    43  	} else {
    44  		switch v1.Kind() {
    45  		case reflect.Struct:
    46  			if v1.NumField() == 0 {
    47  				// We don't know how to compare that
    48  				return false
    49  			}
    50  			for i := 0; i < v1.NumField(); i++ {
    51  				r := deepCompareFields(v1.Field(i), v2.Field(i))
    52  				if !r {
    53  					return r
    54  				}
    55  			}
    56  		case reflect.Slice, reflect.Array:
    57  			if v1.Len() != v2.Len() {
    58  				return false
    59  			}
    60  			if v1.Len() == 0 {
    61  				return true
    62  			}
    63  			if v1.Index(0).Kind() != v2.Index(0).Kind() {
    64  				return false
    65  			}
    66  			for i := 0; i < v1.Len(); i++ {
    67  				e1 := v1.Index(i)
    68  				e2 := v2.Index(i)
    69  				if !deepCompareFields(e1, e2) {
    70  					return false
    71  				}
    72  			}
    73  		case reflect.Map:
    74  			if v1.Len() != v2.Len() {
    75  				return false
    76  			}
    77  			if v1.Len() == 0 {
    78  				return true
    79  			}
    80  
    81  			keys1 := v1.MapKeys()
    82  			for _, k := range keys1 {
    83  				val1 := v1.MapIndex(k)
    84  				val2 := v2.MapIndex(k)
    85  				if !deepCompareFields(val1, val2) {
    86  					return false
    87  				}
    88  			}
    89  			return true
    90  		case reflect.Func:
    91  			return v1.Pointer() == v2.Pointer()
    92  		default:
    93  			fmt.Printf("unsupported v1.Kind=%v t1.Name=%v, t1.Value=%v\n\n", v1.Kind(), v1.Type().Name(), v1.String())
    94  			return false
    95  		}
    96  	}
    97  	return true
    98  }
    99  
   100  func MetricDataIsEqual(d1, d2 *types.MetricData, compareTags bool) bool {
   101  	v1 := reflect.ValueOf(*d1)
   102  	v2 := reflect.ValueOf(*d2)
   103  
   104  	for i := 0; i < v1.NumField(); i++ {
   105  		if v1.Type().Field(i).Name == "Tags" && !compareTags {
   106  			continue
   107  		}
   108  		r := deepCompareFields(v1.Field(i), v2.Field(i))
   109  		if !r {
   110  			return r
   111  		}
   112  	}
   113  	return true
   114  }
   115  
   116  const eps = 0.0000000001
   117  
   118  func NearlyEqual(a, b []float64) bool {
   119  	if len(a) != len(b) {
   120  		return false
   121  	}
   122  
   123  	for i, v := range a {
   124  		// "same"
   125  		if math.IsNaN(a[i]) && math.IsNaN(b[i]) {
   126  			continue
   127  		}
   128  		if math.IsNaN(a[i]) || math.IsNaN(b[i]) {
   129  			// unexpected NaN
   130  			return false
   131  		}
   132  		// "close enough"
   133  		if math.Abs(v-b[i]) > eps {
   134  			return false
   135  		}
   136  	}
   137  
   138  	return true
   139  }
   140  
   141  func NearlyEqualMetrics(a, b *types.MetricData) bool {
   142  	if len(a.Values) != len(b.Values) {
   143  		return false
   144  	}
   145  	for i := range a.Values {
   146  		if (math.IsNaN(a.Values[i]) && !math.IsNaN(b.Values[i])) || (!math.IsNaN(a.Values[i]) && math.IsNaN(b.Values[i])) {
   147  			return false
   148  		}
   149  		// "close enough"
   150  		if math.Abs(a.Values[i]-b.Values[i]) > eps {
   151  			return false
   152  		}
   153  	}
   154  
   155  	return true
   156  }
   157  
   158  func MaxInt(a, b int) int {
   159  	if a >= b {
   160  		return a
   161  	} else {
   162  		return b
   163  	}
   164  }
   165  
   166  func GenerateMetrics(n int, startValue, endValue, stepValue float64) []float64 {
   167  	values := make([]float64, n)
   168  	v := startValue
   169  	for i := 0; i < n; i++ {
   170  		values[i] = v
   171  		v += stepValue
   172  		if v > endValue {
   173  			v = startValue
   174  		}
   175  	}
   176  	return values
   177  }
   178  
   179  func TestMetricData(t *testing.T, got, want []*types.MetricData) {
   180  	for i := 0; i < MaxInt(len(want), len(got)); i++ {
   181  		if i >= len(got) {
   182  			t.Errorf("\n-[%d] = %v", i, want[i])
   183  		} else if i >= len(want) {
   184  			t.Errorf("\n+[%d] = %v", i, got[i])
   185  		} else {
   186  			actual := got[i]
   187  			if _, ok := actual.Tags["name"]; !ok {
   188  				t.Errorf("metric %+v with name %v doesn't contain 'name' tag", actual, actual.Name)
   189  			}
   190  			if actual == nil {
   191  				t.Errorf("returned no value")
   192  				return
   193  			}
   194  			if actual.StepTime == 0 {
   195  				t.Errorf("missing Step for %+v", actual)
   196  			}
   197  			if actual.Name != want[i].Name {
   198  				t.Errorf("bad Name metric[%d]: got %s, want %s", i, actual.Name, want[i].Name)
   199  			}
   200  			if !NearlyEqualMetrics(actual, want[i]) {
   201  				t.Errorf("different values metric[%d] %s: got %v, want %v", i, actual.Name, actual.Values, want[i].Values)
   202  			}
   203  			if actual.StepTime != want[i].StepTime {
   204  				t.Errorf("different StepTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StepTime, want[i].StepTime)
   205  			}
   206  			if actual.StartTime != want[i].StartTime {
   207  				t.Errorf("different StartTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StartTime, want[i].StartTime)
   208  			}
   209  			if actual.StopTime != want[i].StopTime {
   210  				t.Errorf("different StopTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StopTime, want[i].StopTime)
   211  			}
   212  		}
   213  	}
   214  }
   215  
   216  // Validate test case values length
   217  func TestMetricDataEqLen(t *testing.T, got, want []*types.MetricData) {
   218  	length := len(want[0].Values)
   219  	for i := 0; i < MaxInt(len(want), len(got)); i++ {
   220  		if i >= len(got) {
   221  			t.Errorf("\n-[%d] = %v", i, want[i])
   222  		} else if i >= len(want) {
   223  			t.Errorf("\n+[%d] = %v", i, got[i])
   224  		} else {
   225  			if length != len(want[i].Values) {
   226  				t.Fatalf("metric[%d] with name %v contain invalid test case (values length %d not equal with metric[0] values legth %d", i, want[i].Name, length, len(want[i].Values))
   227  			}
   228  			actual := got[i]
   229  			if _, ok := actual.Tags["name"]; !ok {
   230  				t.Errorf("metric %+v with name %v doesn't contain 'name' tag", actual, actual.Name)
   231  			}
   232  			if actual == nil {
   233  				t.Errorf("returned no value")
   234  				return
   235  			}
   236  			if actual.StepTime == 0 {
   237  				t.Errorf("missing Step for %+v", actual)
   238  			}
   239  			if actual.Name != want[i].Name {
   240  				t.Errorf("bad Name metric[%d]: got %s, want %s", i, actual.Name, want[i].Name)
   241  			}
   242  			if !NearlyEqualMetrics(actual, want[i]) {
   243  				t.Errorf("different values metric[%d] %s: got %v, want %v", i, actual.Name, actual.Values, want[i].Values)
   244  			}
   245  			if actual.StepTime != want[i].StepTime {
   246  				t.Errorf("different StepTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StepTime, want[i].StepTime)
   247  			}
   248  			if actual.StartTime != want[i].StartTime {
   249  				t.Errorf("different StartTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StartTime, want[i].StartTime)
   250  			}
   251  			if actual.StopTime != want[i].StopTime {
   252  				t.Errorf("different StopTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StopTime, want[i].StopTime)
   253  			}
   254  		}
   255  	}
   256  }