TEST(ObjectSerializationTest, DynamicSealedAnonymousObject) { AmfObjectTraits traits("", true, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfString("value")); obj.addDynamicProperty("dynamicProp", AmfString("dynamicValue")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x1b, // 0b11011, U29O-traits, dynamic, 1 sealed property 0x01, // class-name "" (anonymous object) // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property values // AmfString "value" 0x06, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65, // dynamic members // UTF-8-vr "dynamicProp" 0x17, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x70, // AmfString "dynamicValue" 0x06, 0x19, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, // end of dynamic members 0x01 }, obj); }
TEST(SerializerTest, MultipleStringValues) { Serializer s; s << AmfString("bar") << AmfString("boofar"); v8 expected { 0x06, 0x07, 0x62, 0x61, 0x72, 0x06, 0x0D, 0x62, 0x6F, 0x6F, 0x66, 0x61, 0x72 }; ASSERT_EQ(expected, s.data()); }
TEST(DictionaryDeserialization, FlashBug) { AmfDictionary d(false, false); d.insert(AmfString("x"), AmfString("g")); v8 data { 0x11, 0x03, 0x00, 0x06, 0x03, 0x78, 0x06, 0x03, 0x67 }; // Flash deserializes this to a dictionary with .x = undefined ... deserialize(d, data, 0); }
TEST(XmlSerializationTest, SerializationCacheNotShared) { SerializationContext ctx; isEqual(v8 { 0x0b, 0x01 }, AmfXml("").serialize(ctx)); isEqual(v8 { 0x0b, 0x07, 0x66, 0x6f, 0x6f }, AmfXml("foo").serialize(ctx)); isEqual(v8 { 0x0b, 0x00 }, AmfXml("").serialize(ctx)); isEqual(v8 { 0x0b, 0x02 }, AmfXml("foo").serialize(ctx)); isEqual(v8 { 0x07, 0x07, 0x67, 0x6f, 0x6f }, AmfXmlDocument("goo").serialize(ctx)); isEqual(v8 { 0x0b, 0x00 }, AmfXml("").serialize(ctx)); isEqual(v8 { 0x0b, 0x02 }, AmfXml("foo").serialize(ctx)); isEqual(v8 { 0x0b, 0x07, 0x67, 0x6f, 0x6f }, AmfXml("goo").serialize(ctx)); isEqual(v8 { 0x06, 0x07, 0x66, 0x6f, 0x6f }, AmfString("foo").serialize(ctx)); isEqual(v8 { 0x06, 0x07, 0x64, 0x6f, 0x6f }, AmfString("doo").serialize(ctx)); isEqual(v8 { 0x0b, 0x07, 0x64, 0x6f, 0x6f }, AmfXml("doo").serialize(ctx)); }
TEST(ObjectSerializationTest, SealedAnonymousObject) { { AmfObjectTraits traits("", false, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfString("value")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x13, // 0b10011, U29O-traits, not dynamic, 1 sealed property 0x01, // class-name "" (anonymous object) // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property values // AmfString "value" 0x06, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65 // no dynamic members, so no empty string }, obj); } { AmfObjectTraits traits("", false, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfString("value")); obj.addSealedProperty("otherSealedProp", AmfString("otherValue")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x23, // 0b100011, U29O-traits, not dynamic, 2 sealed properties 0x01, // class-name "" (anonymous object) // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // UTF-8-vr "otherSealedProp" 0x1f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x53, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed propety values // AmfString "value" 0x06, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65, // AmfString "otherValue" 0x06, 0x15, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65 // no dynamic members }, obj); } }
TEST(DictionaryDeserialization, ObjectKeys) { AmfDictionary d(false, false); d.insert(AmfObject("", true, false), AmfString("foo")); v8 data { 0x11, 0x03, 0x00, 0x0a, 0x0b, 0x01, 0x01, 0x06, 0x07, 0x66, 0x6f, 0x6f }; deserialize(d, data, 0); d = AmfDictionary(false, false); AmfObject o("", true, false); o.addDynamicProperty("bar", AmfInteger(1)); d.insert(o, AmfString("foo")); data = { 0x11, 0x03, 0x00, 0x0a, 0x0b, 0x01, 0x07, 0x62, 0x61, 0x72, 0x04, 0x01, 0x01, 0x06, 0x07, 0x66, 0x6f, 0x6f }; deserialize(d, data, 0); }
TEST(DictionaryDeserialization, StringKeys) { AmfDictionary d(false, false); d.insert(AmfString("foo"), AmfUndefined()); v8 data { 0x11, 0x03, 0x00, 0x06, 0x07, 0x66, 0x6f, 0x6f, 0x00 }; deserialize(d, data, 0); }
TEST(ObjectSerializationTest, OnlySerializeDynamicPropsOnDynamicObjects) { // non-dynamic object AmfObject obj("", false, false); obj.addDynamicProperty("dynamicProp", AmfString("val")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x03, // 0b0011, U29O-traits, not dynamic, 0 sealed properties 0x01 // class-name "" (anonymous object) }, obj); }
TEST(DictionaryEquality, SimpleValues) { AmfDictionary d0(true), d1(true), d2(false); d0.insert(AmfInteger(0), AmfString("foo")); d1.insert(AmfInteger(0), AmfString("foo")); d2.insert(AmfInteger(0), AmfString("foo")); EXPECT_EQ(d0, d1); EXPECT_NE(d0, d2); d0.insert(AmfString("qux"), AmfByteArray(v8 { 0x00 })); EXPECT_NE(d0, d1); d1.insert(AmfString("qux"), AmfByteArray(v8 { 0x00 })); EXPECT_EQ(d0, d1); d0.insert(AmfNull(), AmfUndefined()); d1.insert(AmfUndefined(), AmfNull()); EXPECT_NE(d0, d1); d0.insert(AmfUndefined(), AmfNull()); d1.insert(AmfNull(), AmfUndefined()); EXPECT_EQ(d0, d1); }
TEST(SerializerTest, MultipleMixedValues) { Serializer s; s << AmfDouble(0.5) << AmfInteger(0x3ff) << AmfString("bar") << AmfNull(); v8 expected { 0x05, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x87, 0x7F, 0x06, 0x07, 0x62, 0x61, 0x72, 0x01 }; ASSERT_EQ(expected, s.data()); }
TEST(DictionarySerializationTest, SelfReference) { AmfItemPtr ptr(AmfDictionary(false, false)); ptr.as<AmfDictionary>().values[AmfItemPtr(AmfString("x"))] = ptr; SerializationContext ctx; isEqual({ 0x11, 0x03, 0x00, 0x06, 0x03, 0x78, 0x11, 0x00 }, ptr->serialize(ctx)); }
TEST(DictionaryDeserialization, NestedDictionary) { AmfDictionary d(false, true); AmfDictionary val(false, true); val.insert(AmfString("true"), AmfBool(true)); d.insert(AmfDictionary(false, false), val); v8 data { 0x11, 0x03, 0x01, 0x11, 0x01, 0x00, 0x11, 0x03, 0x01, 0x06, 0x09, 0x74, 0x72, 0x75, 0x65, 0x03 }; deserialize(d, data); }
TEST(DictionarySerializationTest, NumberAsStringsDoesntAffectObjects) { SerializationContext ctx; AmfDictionary d(true); d.insert(AmfArray(), AmfDictionary(false)); d.insert(AmfArray(std::vector<AmfInteger> { 1 }), AmfObject("", true, false)); consistsOf(std::vector<v8> { { // header 0x11, 0x05, // 2 elements 0x00 // no weak keys }, { // [] = {} 0x09, 0x01, 0x01, // empty array 0x11, 0x01, 0x00 // empty array }, { // [1] = {} 0x09, 0x03, 0x01, 0x04, 0x01, // AmfArray [1] 0x0a, 0x0b, 0x01, 0x01 // empty dynamic anonymous object } }, d.serialize(ctx)); AmfObject obj("foo", true, false); obj.addDynamicProperty("prop", AmfString("val")); AmfVector<int> vec { { 1, 2, 3 }, false }; d = AmfDictionary(true); d.insert(obj, vec); isEqual({ 0x11, 0x03, // 1 element 0x00, // no weak keys // key 0x0a, // AMF_OBJECT 0x0b, // U29O-traits | dynamic, 0 sealed properties 0x07, 0x66, 0x6f, 0x6f, // class-name "foo" // dynamic-member 0x09, 0x70, 0x72, 0x6f, 0x70, // UTF-8-vr "prop" 0x06, 0x07, 0x76, 0x61, 0x6c, // AmfString "val" 0x01, // end of object (UTF-8-empty) // value 0x0d, 0x07, 0x00, // AmfVector<int> with 3 elements 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03 }, d); }
TEST(DictionaryDeserialization, ObjectsCorrectReferenceOrder) { // circular object reference to d is index 0 -> { 0x0a, 0x02 } == o AmfDictionary d(false, false); AmfObject o("", true, false); o.addDynamicProperty("bar", AmfInteger(1)); d.insert(o, AmfString("foo")); d.insert(AmfString("qux"), o); v8 data { 0x11, 0x05, 0x00, // key 1 0x0a, 0x0b, 0x01, 0x07, 0x62, 0x61, 0x72, 0x04, 0x01, 0x01, // value 1 0x06, 0x07, 0x66, 0x6f, 0x6f, // key 2 0x06, 0x07, 0x71, 0x75, 0x78, // value 2 0x0a, 0x02 }; deserialize(d, data, 0); }
TEST(ObjectSerializationTest, DynamicAnonymousObject) { AmfObjectTraits traits("", true, false); AmfObject obj(traits); obj.addDynamicProperty("prop", AmfString("val")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x0b, // U29O-traits | dynamic, 0 sealed properties 0x01, // class-name "" (anonymous object) // dynamic-member 0x09, 0x70, 0x72, 0x6f, 0x70, // UTF-8-vr "prop" 0x06, 0x07, 0x76, 0x61, 0x6c, // AmfString "val" 0x01 // end of object (UTF-8-empty) }, obj); }
TEST(DictionarySerializationTest, OverwriteKeys) { AmfDictionary d(true, false); d.insert(AmfBool(false), AmfInteger(3)); isEqual({ 0x11, 0x03, 0x00, 0x06, 0x0B, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x04, 0x03 }, d); d.insert(AmfBool(false), AmfString("foo")); isEqual({ 0x11, 0x03, 0x00, 0x06, 0x0B, 0x66, 0x61, 0x6C, 0x73, 0x65, 0x06, 0x07, 0x66, 0x6f, 0x6f }, d); }
TEST(DictionaryDeserialization, IntegerKeys) { AmfDictionary d(false, false); d.insert(AmfInteger(3), AmfBool(false)); v8 data { 0x11, 0x03, 0x00, 0x04, 0x03, 0x02, }; deserialize(d, data, 0); d = AmfDictionary(false, false); d.insert(AmfInteger(-16384), AmfString("foo")); data = { 0x11, 0x03, 0x00, 0x04, 0xFF, 0xFF, 0xC0, 0x00, 0x06, 0x07, 0x66, 0x6F, 0x6F }; deserialize(d, data, 0); }
TEST(DictionarySerializationTest, MultipleKeys) { AmfDictionary d(true, false); d.insert(AmfInteger(3), AmfBool(false)); d.insert(AmfInteger(-16384), AmfString("foo")); consistsOf(std::vector<v8> { { // header 0x11, // AMF_DICTIONARY 0x05, // 2 elements 0x00, // no weak keys }, { // "3" = false 0x06, 0x03, 0x33, 0x02, }, { // "-16384" = "foo" 0x06, 0x0D, 0x2D, 0x31, 0x36, 0x33, 0x38, 0x34, 0x06, 0x07, 0x66, 0x6F, 0x6F } }, d.serialize()); }
TEST(ArraySerializationTest, AssociativeDenseArray) { std::map<std::string, AmfInteger> sparse; sparse["sparseVal"] = AmfInteger(0xbeef); AmfArray array(std::vector<AmfString> { AmfString("foobar") }, sparse); isEqual(v8 { 0x09, // AMF_ARRAY 0x03, // 1 dense element // assoc-values // UTF-8-vr "sparseVal" 0x13, 0x73, 0x70, 0x61, 0x72, 0x73, 0x65, 0x56, 0x61, 0x6c, // AmfInteger 0xbeef 0x04, 0x82, 0xfd, 0x6f, 0x01, // end of assoc-values // dense elements // AmfString "foobar" 0x06, 0x0d, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, }, array); }
TEST(ObjectSerializationTest, SealedNamedObject) { AmfObjectTraits traits("de.ventero.AmfTest", false, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfString("value")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x13, // 0b10011, U29O-traits, not dynamic, 1 sealed property // class-name UTF-8-vr "de.ventero.AmfTest" 0x25, 0x64, 0x65, 0x2e, 0x76, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x6f, 0x2e, 0x41, 0x6d, 0x66, 0x54, 0x65, 0x73, 0x74, // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property values // AmfString "value" 0x06, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65 // no dynamic members }, obj); }
TEST(ObjectSerializationTest, SerializeOnlyPropsInTraits) { AmfObjectTraits traits("", false, false); AmfObject obj(traits); obj.addSealedProperty("sealedProp", AmfInteger(0x05ffeffe)); // this should not be serialized as it's not part of the trait attributes obj.sealedProperties["unusedProp"] = AmfString("unused").serialize(); isEqual(v8 { 0x0a, // AMF_OBJECT 0x13, // 0b10011, U29O-traits, not dynamic, 1 sealed property 0x01, // class-name "" (anonymous object) // sealed property names // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property values // AmfInteger 0x05ffeffe 0x04, 0x97, 0xff, 0xef, 0xfe // no dynamic members }, obj); }
TEST(DictionarySerializationTest, IntegerKeys) { AmfDictionary d(false, false); d.insert(AmfInteger(3), AmfBool(false)); isEqual(v8 { 0x11, // AMF_DICTIONARY 0x03, // 1 element 0x00, // no weak keys 0x04, 0x03, // AmfInteger 3 0x02 // AmfBool false }, d); d = AmfDictionary(false, false); d.insert(AmfInteger(-16384), AmfString("foo")); isEqual(v8 { 0x11, // AMF_DICTIONARY 0x03, // 1 element 0x00, // no weak keys 0x04, 0xFF, 0xFF, 0xC0, 0x00, 0x06, 0x07, 0x66, 0x6F, 0x6F }, d); }
TEST(DictionarySerializationTest, IntegerAsStringKeys) { AmfDictionary d(true, false); d.insert(AmfInteger(3), AmfBool(false)); isEqual(v8 { 0x11, // AMF_DICTIONARY 0x03, // 1 element 0x00, // no weak keys 0x06, 0x03, 0x33, // AmfInteger 3 serialized as AmfString "3" 0x02 // AmfBool false }, d); d = AmfDictionary(true, false); d.insert(AmfInteger(-16384), AmfString("foo")); isEqual(v8 { 0x11, // AMF_DICTIONARY 0x03, // 1 element 0x00, // no weak keys 0x06, 0x0D, 0x2D, 0x31, 0x36, 0x33, 0x38, 0x34, 0x06, 0x07, 0x66, 0x6F, 0x6F }, d); }
TEST(ObjectSerializationTest, NonTraitCtor) { AmfObject obj; obj.addSealedProperty("sealedProp", AmfInteger(0x7b)); isEqual(v8 { 0x0a, // AMF_OBJECT 0x13, // U29O-traits, not dynamic, 1 sealed prop 0x01, // class-name "" // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property value // AmfInteger 0x7b 0x04, 0x7b // no dynamic members }, obj); obj = AmfObject("foo", true, false); obj.addSealedProperty("sealedProp", AmfInteger(0x7b)); obj.addDynamicProperty("dynamicProp", AmfString("dyn")); isEqual(v8 { 0x0a, // AMF_OBJECT 0x1b, // U29O-traits, dynamic, 1 sealed prop // class-name UTF-8-vr "foo" 0x07, 0x66, 0x6f, 0x6f, // UTF-8-vr "sealedProp" 0x15, 0x73, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, // sealed property value // AmfInteger 0x7b 0x04, 0x7b, // 1 dynamic member // UTF-8-vr "dynamicProp" 0x17, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x70, // AmfString "dyn" 0x06, 0x07, 0x64, 0x79, 0x6e, // end of dynamic members 0x01 }, obj); }
std::vector<u8> AmfVector<AmfItem>::serialize(SerializationContext& ctx) const { int index = ctx.getIndex(*this); if (index != -1) return std::vector<u8> { AMF_VECTOR_OBJECT, u8(index << 1) }; ctx.addObject(*this); // U29V value, encoding the length std::vector<u8> buf = AmfInteger::asLength(values.size(), AMF_VECTOR_OBJECT); // fixed-vector marker buf.push_back(fixed ? 0x01 : 0x00); // object type name std::vector<u8> typeName = AmfString(type).serializeValue(ctx); buf.insert(buf.end(), typeName.begin(), typeName.end()); for (const auto& it : values) { auto s = it->serialize(ctx); buf.insert(buf.end(), s.begin(), s.end()); } return buf; }
static void isEqual(const std::vector<u8>& expected, std::string value) { ASSERT_EQ(expected, AmfString(value).serialize()) << "Failed to encode " << value; }
TEST(AmfString, NullptrCtor) { ASSERT_NO_THROW(AmfString(nullptr)); }
static void isEqual(const std::vector<u8>& expected, const char* value) { isEqual(expected, AmfString(value)); }
AmfString AmfString::deserialize(v8::const_iterator& it, v8::const_iterator end, SerializationContext& ctx) { if (it == end || *it++ != AMF_STRING) throw std::invalid_argument("AmfString: Invalid type marker"); return AmfString(deserializeValue(it, end, ctx)); }
TEST(DeserializerTest, String) { deserializesTo(AmfString("foo"), { 0x06, 0x07, 0x66, 0x6f, 0x6f }); deserializesTo(AmfString("foo"), { 0x06, 0x07, 0x66, 0x6f, 0x6f, 0x06 }, 1); }