Commit e8189e5f authored by Kirill Smelkov's avatar Kirill Smelkov Committed by Kamil Kisiel

tests: Merge Encode and Decode tests data

Previously there were two separate tables - for decode and encode tests.

The table for encode tests was very small. TestDecode, which was
operating on data from decode table, was also performing checks that
decode(encode(object)) is idempotent - the work which is already too by
TestEncode. However the coverage of input objects from decode table was
not a strict superset of encode table objects.

For the reasons above let's stop this divergence. Let's have a common
table that define tests where for every test case there can be:

	- an "in" object,
	- a pickle, and
	- an "out" object

where

	1. pickle must decode to "out" object, and
	2. encoding "in" object must give some pickle that decodes to "out" object.

	NOTE: In the usual case "in" object == "out" object and they can only
	differ if "in" object contains a Go struct.

This will allow us to cover all existing decode and encode tests logic.

However the coverage of each logic is now higher - for example Encoder
tests are now run on every object from main table, not only for 3 cases
like it was before.
parent 05c5233f
......@@ -55,89 +55,151 @@ var graphiteObject3 = []interface{}{map[interface{}]interface{}{"intervals": []i
const longLine = "28,34,30,55,100,130,87,169,194,202,232,252,267,274,286,315,308,221,358,368,401,406,434,452,475,422,497,530,517,559,400,418,571,578,599,600,625,630,635,647,220,715,736,760,705,785,794,495,808,852,861,863,869,875,890,893,896,922,812,980,1074,1087,1145,1153,1163,1171,445,1195,1203,1242,1255,1274,52,1287,1319,636,1160,1339,1345,1353,1369,1391,1396,1405,1221,1410,1431,1451,1460,1470,1472,1492,1517,1528,419,1530,1532,1535,1573,1547,1574,1437,1594,1595,847,1551,983,1637,1647,1666,1672,1691,1726,1515,1731,1739,1741,1723,1776,1685,505,1624,1436,1890,728,1910,1931,1544,2013,2025,2030,2043,2069,1162,2129,2160,2199,2210,1911,2246,804,2276,1673,2299,2315,2322,2328,2355,2376,2405,1159,2425,2430,2452,1804,2442,2567,2577,1167,2611,2534,1879,2623,2682,2699,2652,2742,2754,2774,2782,2795,2431,2821,2751,2850,2090,513,2898,592,2932,2933,1555,2969,3003,3007,3010,2595,3064,3087,3105,3106,3110,151,3129,3132,304,3173,3205,3233,3245,3279,3302,3307,714,316,3331,3347,3360,3375,3380,3442,2620,3482,3493,3504,3516,3517,3518,3533,3511,2681,3530,3601,3606,3615,1210,3633,3651,3688,3690,3781,1907,3839,3840,3847,3867,3816,3899,3924,2345,3912,3966,982,4040,4056,4076,4084,4105,2649,4171,3873,1415,3567,4188,4221,4227,4231,2279,4250,4253,770,894,4343,4356,4289,4404,4438,2572,3124,4334,2114,3953,4522,4537,4561,4571,641,4629,4640,4664,4687,4702,4709,4740,4605,4746,4768,3856,3980,4814,2984,4895,4908,1249,4944,4947,4979,4988,4995,32,4066,5043,4956,5069,5072,5076,5084,5085,5137,4262,5152,479,5156,3114,1277,5183,5186,1825,5106,5216,963,5239,5252,5218,5284,1980,1972,5352,5364,5294,5379,5387,5391,5397,5419,5434,5468,5471,3350,5510,5522,5525,5538,5554,5573,5597,5610,5615,5624,842,2851,5641,5655,5656,5658,5678,5682,5696,5699,5709,5728,5753,851,5805,3528,5822,801,5855,2929,5871,5899,5918,5925,5927,5931,5935,5939,5958,778,5971,5980,5300,6009,6023,6030,6032,6016,6110,5009,6155,6197,1760,6253,6267,4886,5608,6289,6308,6311,6321,6316,6333,6244,6070,6349,6353,6186,6357,6366,6386,6387,6389,6399,6411,6421,6432,6437,6465,6302,6493,5602,6511,6529,6536,6170,6557,6561,6577,6581,6590,5290,5649,6231,6275,6635,6651,6652,5929,6692,6693,6695,6705,6711,6723,6738,6752,6753,3629,2975,6790,5845,338,6814,6826,6478,6860,6872,6882,880,356,6897,4102,6910,6611,1030,6934,6936,6987,6984,6999,827,6902,7027,7049,7051,4628,7084,7083,7071,7102,7137,5867,7152,6048,2410,3896,7168,7177,7224,6606,7233,1793,7261,7284,7290,7292,5212,7315,6964,3238,355,1969,4256,448,7325,908,2824,2981,3193,3363,3613,5325,6388,2247,1348,72,131,5414,7285,7343,7349,7362,7372,7381,7410,7418,7443,5512,7470,7487,7497,7516,7277,2622,2863,945,4344,3774,1024,2272,7523,4476,256,5643,3164,7539,7540,7489,1932,7559,7575,7602,7605,7609,7608,7619,7204,7652,7663,6907,7672,7654,7674,7687,7718,7745,1202,4030,7797,7801,7799,2924,7871,7873,7900,7907,7911,7912,7917,7923,7935,8007,8017,7636,8084,8087,3686,8114,8153,8158,8171,8175,8182,8205,8222,8225,8229,8232,8234,8244,8247,7256,8279,6929,8285,7040,8328,707,6773,7949,8468,5759,6344,8509,1635"
var tests = []struct {
name string
input string
expected interface{}
}{
{"int", "I5\n.", int64(5)},
{"float", "F1.23\n.", float64(1.23)},
{"long", "L12321231232131231231L\n.", bigInt("12321231232131231231")},
{"None", "N.", None{}},
{"empty tuple", "(t.", Tuple{}},
{"tuple of two ints", "(I1\nI2\ntp0\n.", Tuple{int64(1), int64(2)}},
{"nested tuples", "((I1\nI2\ntp0\n(I3\nI4\ntp1\ntp2\n.",
Tuple{Tuple{int64(1), int64(2)}, Tuple{int64(3), int64(4)}}},
{"tuple with top 1 items from stack", "I0\n\x85.", Tuple{int64(0)}},
{"tuple with top 2 items from stack", "I0\nI1\n\x86.", Tuple{int64(0), int64(1)}},
{"tuple with top 3 items from stack", "I0\nI1\nI2\n\x87.", Tuple{int64(0), int64(1), int64(2)}},
{"empty list", "(lp0\n.", []interface{}{}},
{"list of numbers", "(lp0\nI1\naI2\naI3\naI4\na.", []interface{}{int64(1), int64(2), int64(3), int64(4)}},
{"string", "S'abc'\np0\n.", string("abc")},
{"unicode", "V\\u65e5\\u672c\\u8a9e\np0\n.", string("日本語")},
{"unicode2", "V' \\u77e5\\u4e8b\\u5c11\\u65f6\\u70e6\\u607c\\u5c11\\u3001\\u8bc6\\u4eba\\u591a\\u5904\\u662f\\u975e\\u591a\\u3002\n.", string("' 知事少时烦恼少、识人多处是非多。")},
{"empty dict", "(dp0\n.", make(map[interface{}]interface{})},
{"dict with strings", "(dp0\nS'a'\np1\nS'1'\np2\nsS'b'\np3\nS'2'\np4\ns.", map[interface{}]interface{}{"a": "1", "b": "2"}},
{"GLOBAL and REDUCE opcodes", "cfoo\nbar\nS'bing'\n\x85R.", Call{Callable: Class{Module: "foo", Name: "bar"}, Args: []interface{}{"bing"}}},
{"LONG_BINPUT opcode", "(lr0000I17\na.", []interface{}{int64(17)}},
{"persistent ref", "Pabc\n.", Ref{"abc"}},
{"bin persistent ref", "\x80\x01U\x05def\ngQ.", Ref{"def\ng"}},
{"bin persistent ref to !string", "\x80\x01(I1\nI2\ntQ.", Ref{Tuple{int64(1), int64(2)}}},
{"graphite message1", graphitePickle1, graphiteObject1},
{"graphite message2", graphitePickle2, graphiteObject2},
{"graphite message3", graphitePickle3, graphiteObject3},
{"too long line", "V" + longLine + "\n.", longLine},
{"FRAME Opcode and int", "\x95\x00\x00\x00\x00\x00\x00\x00\x00I5\n.", int64(5)},
{"SHORTBINUNICODE opcode", "\x8c\t\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\x94.", "日本語"},
{"STACK_GLOBAL opcode", "S'foo'\nS'bar'\n\x93.", Class{Module: "foo", Name: "bar"}},
// TestEntry represents one decode/encode test.
type TestEntry struct {
name string
// object(s) and pickle. Pickle must decode to objectOut.
// Encoding objectIn must give some pickle that decodes to ObjectOut.
//
// In the usual case objectIn == objectOut and they can only differ if
// objectIn contains a Go struct.
objectIn interface{}
pickle string
objectOut interface{}
}
// X is syntatic sugar to prepare one TestEntry.
func X(name string, object interface{}, pickle string) TestEntry {
return TestEntry{name: name, objectIn: object, objectOut: object, pickle: pickle}
}
// Xloosy is syntatic sugar to prepare one TestEntry with loosy incoding.
//
// It should be used only if objectIn contains Go structs.
func Xloosy(name string, objectIn, objectOut interface{}, pickle string) TestEntry {
return TestEntry{name: name, objectIn: objectIn, objectOut: objectOut, pickle: pickle}
}
// tests is the main registry for decode/encode tests.
var tests = []TestEntry{
X("None", None{},
"N."),
X("int(5)", int64(5),
"I5\n."),
X("int(0x123)", int64(0x123),
"I291\n."),
X("int(0x12345)", int64(0x12345),
"I74565\n."),
X("float", float64(1.23),
"F1.23\n."),
X("long", bigInt("12321231232131231231"),
"L12321231232131231231L\n."),
X("tuple()", Tuple{},
"(t."),
X("tuple((1,2))", Tuple{int64(1), int64(2)},
"(I1\nI2\ntp0\n."),
X("tuple with top 1 items from stack", Tuple{int64(0)},
"I0\n\x85."),
X("tuple with top 2 items from stack", Tuple{int64(0), int64(1)},
"I0\nI1\n\x86."),
X("tuple with top 3 items from stack", Tuple{int64(0), int64(1), int64(2)},
"I0\nI1\nI2\n\x87."),
X("tuple(((1,2), (3,4)))", Tuple{Tuple{int64(1), int64(2)}, Tuple{int64(3), int64(4)}},
"((I1\nI2\ntp0\n(I3\nI4\ntp1\ntp2\n."),
X("list([])", []interface{}{},
"(lp0\n."),
X("list([1,2,3,True])", []interface{}{int64(1), int64(2), int64(3), true},
"(lp0\nI1\naI2\naI3\naI01\na."),
X("str('abc')", "abc",
"S'abc'\np0\n."),
X("unicode('日本語')", "日本語",
"V\\u65e5\\u672c\\u8a9e\np0\n."), // UNICODE
X("unicode2('日本語')", "日本語",
"\x8c\t\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\x94."), // SHORT_BINUNICODE
X("unicode('\\' 知事少时烦恼少、识人多处是非多。')", "' 知事少时烦恼少、识人多处是非多。",
"V' \\u77e5\\u4e8b\\u5c11\\u65f6\\u70e6\\u607c\\u5c11\\u3001\\u8bc6\\u4eba\\u591a\\u5904\\u662f\\u975e\\u591a\\u3002\n."),
X("dict({})", make(map[interface{}]interface{}),
"(dp0\n."),
X("dict({'a': '1', 'b': '2'})", map[interface{}]interface{}{"a": "1", "b": "2"},
"(dp0\nS'a'\np1\nS'1'\np2\nsS'b'\np3\nS'2'\np4\ns."),
X("foo.bar # global", Class{Module: "foo", Name: "bar"},
"S'foo'\nS'bar'\n\x93."),
X(`foo.bar("bing") # global + reduce`, Call{Callable: Class{Module: "foo", Name: "bar"}, Args: []interface{}{"bing"}},
"cfoo\nbar\nS'bing'\n\x85R."),
X(`persref("abc")`, Ref{"abc"},
"Pabc\n."),
X(`persref("abc\nd")`, Ref{"abc\nd"},
"U\x05abc\ndQ."),
X(`persref((1, 2))`, Ref{Tuple{int64(1), int64(2)}},
"(I1\nI2\ntQ."),
// decode only
// TODO PUT + GET + BINGET + LONG_BINGET
X("LONG_BINPUT", []interface{}{int64(17)},
"(lr0000I17\na."),
X("graphite message1", graphiteObject1, graphitePickle1),
X("graphite message2", graphiteObject2, graphitePickle2),
X("graphite message3", graphiteObject3, graphitePickle3),
X("too long line", longLine, "V" + longLine + "\n."),
// opcodes from protocol 4
X("FRAME opcode", int64(5),
"\x95\x00\x00\x00\x00\x00\x00\x00\x00I5\n."), // FRAME is just skipped
// loosy encode: decoding back gives another object.
// the only case where ogórek encoding is loosy is for Go struct types.
Xloosy("[]ogórek.foo{\"Qux\", 4}", []foo{{"Qux", 4}},
[]interface{}{map[interface{}]interface{}{"Foo": "Qux", "Bar": int64(4)}},
"((S\"Foo\"\nS\"Qux\"\nS\"Bar\"\nI4\ndl."),
}
// foo is a type to test how encoder handles Go structs.
type foo struct {
Foo string
Bar int32
}
// TestDecode verifies ogórek decoder.
func TestDecode(t *testing.T) {
for _, test := range tests {
t.Run(fmt.Sprintf("%s/%q", test.name, test.input), func(t *testing.T) {
testDecode(t, test.expected, test.input)
t.Run(fmt.Sprintf("%s/%q", test.name, test.pickle), func(t *testing.T) {
testDecode(t, test.objectOut, test.pickle)
})
}
}
// TestEncode verifies ogórek encoder.
func TestEncode(t *testing.T) {
type foo struct {
Foo string
Bar int32
}
tests := []struct {
name string
input interface{}
output interface{}
}{
{
"graphite message",
graphiteObject1,
nil,
},
{
"small types",
[]interface{}{int64(0), int64(1), int64(258), int64(65537), false, true},
nil,
},
{
"array of struct types",
[]foo{{"Qux", 4}},
[]interface{}{map[interface{}]interface{}{"Foo": "Qux", "Bar": int64(4)}},
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%s", test.name), func(t *testing.T) {
objectOut := test.output
if objectOut == nil {
objectOut = test.input
}
testEncode(t, test.input, objectOut)
testEncode(t, test.objectIn, test.objectOut)
})
}
}
......@@ -165,24 +227,6 @@ func testDecode(t *testing.T, object interface{}, input string) {
t.Errorf("decode: no EOF at end: v = %#v err = %#v", v, err)
}
// expected (= decoded(input)) -> encode -> decode = identity
buf.Reset()
enc := NewEncoder(buf)
err = enc.Encode(object)
if err != nil {
t.Errorf("encode(expected): %v", err)
} else {
dec := NewDecoder(buf)
v, err := dec.Decode()
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(v, object) {
t.Errorf("expected -> decode -> encode != identity\nhave: %#v\nwant: %#v", v, object)
}
}
// decode(truncated input) -> must return io.ErrUnexpectedEOF
for l := len(input) - 1; l > 0; l-- {
buf := bytes.NewBufferString(input[:l])
......@@ -486,7 +530,7 @@ func BenchmarkDecode(b *testing.B) {
// prepare one large pickle stream from all test pickles
input := make([]byte, 0)
for _, test := range tests {
input = append(input, test.input...)
input = append(input, test.pickle...)
}
b.ResetTimer()
......@@ -516,8 +560,8 @@ func BenchmarkEncode(b *testing.B) {
input := make([]interface{}, 0)
approxOutSize := 0
for _, test := range tests {
input = append(input, test.expected)
approxOutSize += len(test.input)
input = append(input, test.objectIn)
approxOutSize += len(test.pickle)
}
buf := bytes.NewBuffer(make([]byte, approxOutSize))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment