• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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