github.com/m3db/m3@v1.5.0/src/x/serialize/encode_decode_prop_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package serialize
    22  
    23  import (
    24  	"fmt"
    25  	"testing"
    26  
    27  	"github.com/m3db/m3/src/x/ident"
    28  
    29  	"github.com/leanovate/gopter"
    30  	"github.com/leanovate/gopter/gen"
    31  	"github.com/leanovate/gopter/prop"
    32  )
    33  
    34  var (
    35  	testParams *gopter.TestParameters
    36  )
    37  
    38  func init() {
    39  	testParams = gopter.DefaultTestParameters()
    40  	testParams.MinSuccessfulTests = 10000
    41  	testParams.MaxSize = 12
    42  }
    43  
    44  func TestPropertySerializationBijective(t *testing.T) {
    45  	properties := gopter.NewProperties(testParams)
    46  	properties.Property("serialization is bijiective", prop.ForAll(
    47  		func(x string) (bool, error) {
    48  			tags := ident.NewTagsIterator(ident.NewTags(ident.StringTag(x, x)))
    49  			copy, err := encodeAndDecode(tags)
    50  			if err != nil {
    51  				return false, err
    52  			}
    53  			return tagItersAreEqual(tags, copy)
    54  		},
    55  		gen.AnyString().SuchThat(func(x string) bool { return len(x) > 0 }),
    56  	))
    57  	properties.TestingRun(t)
    58  }
    59  
    60  func TestPropertyAnyStringsDontCollide(t *testing.T) {
    61  	properties := gopter.NewProperties(testParams)
    62  	properties.Property("no collisions during string concat", prop.ForAll(
    63  		func(tag ident.Tag) (bool, error) {
    64  			tags := ident.NewTagsIterator(ident.NewTags(tag))
    65  			copy, err := encodeAndDecode(tags)
    66  			if err != nil {
    67  				return false, err
    68  			}
    69  			return tagItersAreEqual(tags, copy)
    70  		}, anyTag(),
    71  	))
    72  
    73  	properties.TestingRun(t)
    74  }
    75  
    76  func TestPropertyAnyReasonableTagSlicesAreAight(t *testing.T) {
    77  	properties := gopter.NewProperties(testParams)
    78  	properties.Property("tags of reasonable length are handled fine", prop.ForAll(
    79  		func(tags ident.Tags) (bool, error) {
    80  			iter := ident.NewTagsIterator(tags)
    81  			copy, err := encodeAndDecode(iter)
    82  			if err != nil {
    83  				return false, err
    84  			}
    85  			return tagItersAreEqual(iter, copy)
    86  		},
    87  		anyTags().WithLabel("input tags"),
    88  	))
    89  
    90  	properties.TestingRun(t)
    91  }
    92  
    93  func encodeAndDecode(t ident.TagIterator) (ident.TagIterator, error) {
    94  	copy := t.Duplicate()
    95  	enc := newTagEncoder(defaultNewCheckedBytesFn, newTestEncoderOpts(), nil)
    96  	if err := enc.Encode(copy); err != nil {
    97  		return nil, err
    98  	}
    99  	data, ok := enc.Data()
   100  	if !ok {
   101  		return nil, fmt.Errorf("unable to retrieve data")
   102  	}
   103  	dec := newTagDecoder(testDecodeOpts, nil)
   104  	dec.Reset(data)
   105  	return dec, nil
   106  }
   107  
   108  func tagItersAreEqual(ti1, ti2 ident.TagIterator) (bool, error) {
   109  	ti1Next := ti1.Next()
   110  	ti2Next := ti2.Next()
   111  
   112  	if ti1Next != ti2Next {
   113  		_, err := iterErrCheck(ti1, ti2)
   114  		return false, fmt.Errorf("un-equal next check, err: %v", err)
   115  	}
   116  
   117  	if !ti1Next && !ti2Next {
   118  		return iterErrCheck(ti1, ti2)
   119  	}
   120  
   121  	t1, t2 := ti1.Current(), ti2.Current()
   122  	if !t1.Name.Equal(t2.Name) {
   123  		return false, fmt.Errorf("tag names are un-equal: %v %v",
   124  			t1.Name.Bytes(), t2.Name.Bytes())
   125  	}
   126  	if !t2.Value.Equal(t2.Value) {
   127  		return false, fmt.Errorf("tag values are un-equal: %v %v",
   128  			t1.Value.Bytes(), t2.Value.Bytes())
   129  	}
   130  
   131  	return tagItersAreEqual(ti1, ti2)
   132  }
   133  
   134  func iterErrCheck(ti1, ti2 ident.TagIterator) (bool, error) {
   135  	err1 := ti1.Err()
   136  	err2 := ti2.Err()
   137  	if err1 == nil && err2 == nil {
   138  		return true, nil
   139  	}
   140  	if err2 == err1 {
   141  		return true, nil
   142  	}
   143  	return false, fmt.Errorf("%v %v", err1, err2)
   144  }
   145  
   146  func anyTag() gopter.Gen {
   147  	limit := int(testDecodeOpts.TagSerializationLimits().MaxTagLiteralLength())
   148  	return gopter.CombineGens(gen.Identifier(), gen.Identifier()).
   149  		SuchThat(func(values []interface{}) bool {
   150  			name := values[0].(string)
   151  			if len(name) > limit && len(name) > 0 {
   152  				return false
   153  			}
   154  			value := values[1].(string)
   155  			return len(value) <= limit && len(value) > 0
   156  		}).
   157  		Map(func(values []interface{}) ident.Tag {
   158  			name := values[0].(string)
   159  			value := values[1].(string)
   160  			return ident.StringTag(name, value)
   161  		})
   162  }
   163  
   164  func anyTags() gopter.Gen {
   165  	return gen.SliceOf(anyTag()).
   166  		Map(func(tags []ident.Tag) ident.Tags {
   167  			return ident.NewTags(tags...)
   168  		})
   169  }