1/** 2 * @fileoverview Tests to make sure Kernel can read data in a backward 3 * compatible way even when protobuf schema changes according to the rules 4 * defined in 5 * https://developers.google.com/protocol-buffers/docs/proto#updating and 6 * https://developers.google.com/protocol-buffers/docs/proto3#updating. 7 * 8 * third_party/protobuf/conformance/binary_json_conformance_suite.cc already 9 * covers many compatibility tests, this file covers only the tests not covered 10 * by binary_json_conformance_suite. Ultimately all of the tests in this file 11 * should be moved to binary_json_conformance_suite. 12 */ 13goog.module('protobuf.runtime.KernelCompatibilityTest'); 14 15goog.setTestOnly(); 16 17const ByteString = goog.require('protobuf.ByteString'); 18const Int64 = goog.require('protobuf.Int64'); 19const Kernel = goog.require('protobuf.runtime.Kernel'); 20const TestMessage = goog.require('protobuf.testing.binary.TestMessage'); 21const {CHECK_CRITICAL_STATE} = goog.require('protobuf.internal.checks'); 22 23/** 24 * @param {...number} bytes 25 * @return {!ArrayBuffer} 26 */ 27function createArrayBuffer(...bytes) { 28 return new Uint8Array(bytes).buffer; 29} 30 31/** 32 * Returns the Unicode character codes of a string. 33 * @param {string} str 34 * @return {!Array<number>} 35 */ 36function getCharacterCodes(str) { 37 return Array.from(str, (c) => c.charCodeAt(0)); 38} 39 40describe('optional -> repeated compatibility', () => { 41 it('is maintained for scalars', () => { 42 const oldAccessor = Kernel.createEmpty(); 43 oldAccessor.setInt32(1, 1); 44 const serializedData = oldAccessor.serialize(); 45 expect(serializedData).toEqual(createArrayBuffer(0x8, 0x1)); 46 47 const newAccessor = Kernel.fromArrayBuffer(serializedData); 48 expect(newAccessor.getRepeatedInt32Size(1)).toEqual(1); 49 expect(newAccessor.getRepeatedInt32Element(1, 0)).toEqual(1); 50 }); 51 52 it('is maintained for messages', () => { 53 const message = new TestMessage(Kernel.createEmpty()); 54 message.setInt32(1, 1); 55 56 const oldAccessor = Kernel.createEmpty(); 57 oldAccessor.setMessage(1, message); 58 const serializedData = oldAccessor.serialize(); 59 expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0x8, 0x1)); 60 61 const newAccessor = Kernel.fromArrayBuffer(serializedData); 62 expect(newAccessor.getRepeatedMessageSize(1, TestMessage.instanceCreator)) 63 .toEqual(1); 64 expect( 65 newAccessor.getRepeatedMessageElement(1, TestMessage.instanceCreator, 0) 66 .serialize()) 67 .toEqual(message.serialize()); 68 }); 69 70 it('is maintained for bytes', () => { 71 const message = new TestMessage(Kernel.createEmpty()); 72 message.setInt32(1, 1); 73 74 const oldAccessor = Kernel.createEmpty(); 75 oldAccessor.setBytes( 76 1, ByteString.fromArrayBuffer(createArrayBuffer(0xA, 0xB))); 77 const serializedData = oldAccessor.serialize(); 78 expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0xA, 0xB)); 79 80 const newAccessor = Kernel.fromArrayBuffer(serializedData); 81 expect(newAccessor.getRepeatedBytesSize(1)).toEqual(1); 82 expect(newAccessor.getRepeatedBoolElement(1, 0)) 83 .toEqual(ByteString.fromArrayBuffer(createArrayBuffer(0xA, 0xB))); 84 }); 85 86 it('is maintained for strings', () => { 87 const oldAccessor = Kernel.createEmpty(); 88 oldAccessor.setString(1, 'hello'); 89 const serializedData = oldAccessor.serialize(); 90 expect(serializedData) 91 .toEqual(createArrayBuffer(0xA, 0x5, 0x68, 0x65, 0x6C, 0x6C, 0x6F)); 92 93 const newAccessor = Kernel.fromArrayBuffer(serializedData); 94 expect(newAccessor.getRepeatedStringSize(1)).toEqual(1); 95 expect(newAccessor.getRepeatedStringElement(1, 0)).toEqual('hello'); 96 }); 97}); 98 99describe('Kernel repeated -> optional compatibility', () => { 100 it('is maintained for unpacked scalars', () => { 101 const oldAccessor = Kernel.createEmpty(); 102 oldAccessor.addUnpackedInt32Element(1, 0); 103 oldAccessor.addUnpackedInt32Element(1, 1); 104 const serializedData = oldAccessor.serialize(); 105 expect(serializedData).toEqual(createArrayBuffer(0x8, 0x0, 0x8, 0x1)); 106 107 const newAccessor = Kernel.fromArrayBuffer(serializedData); 108 expect(newAccessor.getInt32WithDefault(1)).toEqual(1); 109 expect(newAccessor.serialize()).toEqual(serializedData); 110 }); 111 112 // repeated -> optional transformation is not supported for packed fields yet: 113 // go/proto-schema-repeated 114 it('is not maintained for packed scalars', () => { 115 const oldAccessor = Kernel.createEmpty(); 116 oldAccessor.addPackedInt32Element(1, 0); 117 oldAccessor.addPackedInt32Element(1, 1); 118 const serializedData = oldAccessor.serialize(); 119 expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0x0, 0x1)); 120 121 const newAccessor = Kernel.fromArrayBuffer(serializedData); 122 if (CHECK_CRITICAL_STATE) { 123 expect(() => newAccessor.getInt32WithDefault(1)).toThrow(); 124 } 125 }); 126 127 it('is maintained for messages', () => { 128 const message1 = new TestMessage(Kernel.createEmpty()); 129 message1.setInt32(1, 1); 130 const message2 = new TestMessage(Kernel.createEmpty()); 131 message2.setInt32(1, 2); 132 message2.setInt32(2, 3); 133 134 const oldAccessor = Kernel.createEmpty(); 135 oldAccessor.addRepeatedMessageElement( 136 1, message1, TestMessage.instanceCreator); 137 oldAccessor.addRepeatedMessageElement( 138 1, message2, TestMessage.instanceCreator); 139 const serializedData = oldAccessor.serialize(); 140 expect(serializedData) 141 .toEqual(createArrayBuffer( 142 0xA, 0x2, 0x8, 0x1, 0xA, 0x4, 0x8, 0x2, 0x10, 0x3)); 143 144 const newAccessor = Kernel.fromArrayBuffer(serializedData); 145 // Values from message1 and message2 have been merged 146 const newMessage = newAccessor.getMessage(1, TestMessage.instanceCreator); 147 expect(newMessage.getRepeatedInt32Size(1)).toEqual(2); 148 expect(newMessage.getRepeatedInt32Element(1, 0)).toEqual(1); 149 expect(newMessage.getRepeatedInt32Element(1, 1)).toEqual(2); 150 expect(newMessage.getInt32WithDefault(2)).toEqual(3); 151 expect(newMessage.serialize()) 152 .toEqual(createArrayBuffer(0x8, 0x1, 0x8, 0x2, 0x10, 0x3)); 153 }); 154 155 it('is maintained for bytes', () => { 156 const oldAccessor = Kernel.createEmpty(); 157 oldAccessor.addRepeatedBytesElement( 158 1, ByteString.fromArrayBuffer(createArrayBuffer(0xA, 0xB))); 159 oldAccessor.addRepeatedBytesElement( 160 1, ByteString.fromArrayBuffer(createArrayBuffer(0xC, 0xD))); 161 const serializedData = oldAccessor.serialize(); 162 expect(serializedData) 163 .toEqual(createArrayBuffer(0xA, 0x2, 0xA, 0xB, 0xA, 0x2, 0xC, 0xD)); 164 165 const newAccessor = Kernel.fromArrayBuffer(serializedData); 166 expect(newAccessor.getBytesWithDefault(1)) 167 .toEqual(ByteString.fromArrayBuffer(createArrayBuffer(0xC, 0xD))); 168 expect(newAccessor.serialize()).toEqual(serializedData); 169 }); 170 171 it('is maintained for strings', () => { 172 const oldAccessor = Kernel.createEmpty(); 173 oldAccessor.addRepeatedStringElement(1, 'hello'); 174 oldAccessor.addRepeatedStringElement(1, 'world'); 175 const serializedData = oldAccessor.serialize(); 176 expect(serializedData) 177 .toEqual(createArrayBuffer( 178 0xA, 0x5, ...getCharacterCodes('hello'), 0xA, 0x5, 179 ...getCharacterCodes('world'))); 180 181 const newAccessor = Kernel.fromArrayBuffer(serializedData); 182 expect(newAccessor.getStringWithDefault(1)).toEqual('world'); 183 expect(newAccessor.serialize()).toEqual(serializedData); 184 }); 185}); 186 187describe('Type change', () => { 188 it('is supported for fixed32 -> sfixed32', () => { 189 const oldAccessor = Kernel.createEmpty(); 190 oldAccessor.setFixed32(1, 4294967295); 191 const serializedData = oldAccessor.serialize(); 192 expect(serializedData) 193 .toEqual(createArrayBuffer(0xD, 0xFF, 0xFF, 0xFF, 0xFF)); 194 195 const newAccessor = Kernel.fromArrayBuffer(serializedData); 196 expect(newAccessor.getSfixed32WithDefault(1)).toEqual(-1); 197 expect(newAccessor.serialize()).toEqual(serializedData); 198 }); 199 200 it('is supported for sfixed32 -> fixed32', () => { 201 const oldAccessor = Kernel.createEmpty(); 202 oldAccessor.setSfixed32(1, -1); 203 const serializedData = oldAccessor.serialize(); 204 expect(serializedData) 205 .toEqual(createArrayBuffer(0xD, 0xFF, 0xFF, 0xFF, 0xFF)); 206 207 const newAccessor = Kernel.fromArrayBuffer(serializedData); 208 expect(newAccessor.getFixed32WithDefault(1)).toEqual(4294967295); 209 expect(newAccessor.serialize()).toEqual(serializedData); 210 }); 211 212 it('is supported for fixed64 -> sfixed64', () => { 213 const oldAccessor = Kernel.createEmpty(); 214 oldAccessor.setFixed64(1, Int64.fromHexString('0xFFFFFFFFFFFFFFFF')); 215 const serializedData = oldAccessor.serialize(); 216 expect(serializedData) 217 .toEqual(createArrayBuffer( 218 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)); 219 220 const newAccessor = Kernel.fromArrayBuffer(serializedData); 221 expect(newAccessor.getSfixed64WithDefault(1)).toEqual(Int64.fromInt(-1)); 222 expect(newAccessor.serialize()).toEqual(serializedData); 223 }); 224 225 it('is supported for sfixed64 -> fixed64', () => { 226 const oldAccessor = Kernel.createEmpty(); 227 oldAccessor.setSfixed64(1, Int64.fromInt(-1)); 228 const serializedData = oldAccessor.serialize(); 229 expect(serializedData) 230 .toEqual(createArrayBuffer( 231 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)); 232 233 const newAccessor = Kernel.fromArrayBuffer(serializedData); 234 expect(newAccessor.getFixed64WithDefault(1)) 235 .toEqual(Int64.fromHexString('0xFFFFFFFFFFFFFFFF')); 236 expect(newAccessor.serialize()).toEqual(serializedData); 237 }); 238 239 it('is supported for bytes -> message', () => { 240 const oldAccessor = Kernel.createEmpty(); 241 oldAccessor.setBytes( 242 1, ByteString.fromArrayBuffer(createArrayBuffer(0x8, 0x1))); 243 const serializedData = oldAccessor.serialize(); 244 expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0x8, 0x1)); 245 246 const newAccessor = Kernel.fromArrayBuffer(serializedData); 247 const message = newAccessor.getMessage(1, TestMessage.instanceCreator); 248 expect(message.getInt32WithDefault(1)).toEqual(1); 249 expect(message.serialize()).toEqual(createArrayBuffer(0x8, 0x1)); 250 expect(newAccessor.serialize()).toEqual(serializedData); 251 }); 252 253 it('is supported for message -> bytes', () => { 254 const oldAccessor = Kernel.createEmpty(); 255 const message = new TestMessage(Kernel.createEmpty()); 256 message.setInt32(1, 1); 257 oldAccessor.setMessage(1, message); 258 const serializedData = oldAccessor.serialize(); 259 expect(serializedData).toEqual(createArrayBuffer(0xA, 0x2, 0x8, 0x1)); 260 261 const newAccessor = Kernel.fromArrayBuffer(serializedData); 262 expect(newAccessor.getBytesWithDefault(1)) 263 .toEqual(ByteString.fromArrayBuffer(createArrayBuffer(0x8, 0x1))); 264 expect(newAccessor.serialize()).toEqual(serializedData); 265 }); 266}); 267