Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/tests/gtest/TestBase64.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "mozilla/Attributes.h"
7
#include "mozilla/Base64.h"
8
#include "nsIScriptableBase64Encoder.h"
9
#include "nsIInputStream.h"
10
#include "nsString.h"
11
12
#include "gtest/gtest.h"
13
14
struct Chunk {
15
  Chunk(uint32_t l, const char* c)
16
    : mLength(l), mData(c)
17
96
  {}
18
19
  uint32_t mLength;
20
  const char* mData;
21
};
22
23
struct Test {
24
  Test(Chunk* c, const char* r)
25
    : mChunks(c), mResult(r)
26
27
  {}
27
28
  Chunk* mChunks;
29
  const char* mResult;
30
};
31
32
static Chunk kTest1Chunks[] =
33
{
34
   Chunk(9, "Hello sir"),
35
   Chunk(0, nullptr)
36
};
37
38
static Chunk kTest2Chunks[] =
39
{
40
   Chunk(3, "Hel"),
41
   Chunk(3, "lo "),
42
   Chunk(3, "sir"),
43
   Chunk(0, nullptr)
44
};
45
46
static Chunk kTest3Chunks[] =
47
{
48
   Chunk(1, "I"),
49
   Chunk(0, nullptr)
50
};
51
52
static Chunk kTest4Chunks[] =
53
{
54
   Chunk(2, "Hi"),
55
   Chunk(0, nullptr)
56
};
57
58
static Chunk kTest5Chunks[] =
59
{
60
   Chunk(1, "B"),
61
   Chunk(2, "ob"),
62
   Chunk(0, nullptr)
63
};
64
65
static Chunk kTest6Chunks[] =
66
{
67
   Chunk(2, "Bo"),
68
   Chunk(1, "b"),
69
   Chunk(0, nullptr)
70
};
71
72
static Chunk kTest7Chunks[] =
73
{
74
   Chunk(1, "F"),    // Carry over 1
75
   Chunk(4, "iref"), // Carry over 2
76
   Chunk(2, "ox"),   //            1
77
   Chunk(4, " is "), //            2
78
   Chunk(2, "aw"),   //            1
79
   Chunk(4, "esom"), //            2
80
   Chunk(2, "e!"),
81
   Chunk(0, nullptr)
82
};
83
84
static Chunk kTest8Chunks[] =
85
{
86
   Chunk(5, "ALL T"),
87
   Chunk(1, "H"),
88
   Chunk(4, "ESE "),
89
   Chunk(2, "WO"),
90
   Chunk(21, "RLDS ARE YOURS EXCEPT"),
91
   Chunk(9, " EUROPA. "),
92
   Chunk(25, "ATTEMPT NO LANDING THERE."),
93
   Chunk(0, nullptr)
94
};
95
96
static Test kTests[] =
97
  {
98
    // Test 1, test a simple round string in one chunk
99
    Test(
100
      kTest1Chunks,
101
      "SGVsbG8gc2ly"
102
    ),
103
    // Test 2, test a simple round string split into round chunks
104
    Test(
105
      kTest2Chunks,
106
      "SGVsbG8gc2ly"
107
    ),
108
    // Test 3, test a single chunk that's 2 short
109
    Test(
110
      kTest3Chunks,
111
      "SQ=="
112
    ),
113
    // Test 4, test a single chunk that's 1 short
114
    Test(
115
      kTest4Chunks,
116
      "SGk="
117
    ),
118
    // Test 5, test a single chunk that's 2 short, followed by a chunk of 2
119
    Test(
120
      kTest5Chunks,
121
      "Qm9i"
122
    ),
123
    // Test 6, test a single chunk that's 1 short, followed by a chunk of 1
124
    Test(
125
      kTest6Chunks,
126
      "Qm9i"
127
    ),
128
    // Test 7, test alternating carryovers
129
    Test(
130
      kTest7Chunks,
131
      "RmlyZWZveCBpcyBhd2Vzb21lIQ=="
132
    ),
133
    // Test 8, test a longish string
134
    Test(
135
      kTest8Chunks,
136
      "QUxMIFRIRVNFIFdPUkxEUyBBUkUgWU9VUlMgRVhDRVBUIEVVUk9QQS4gQVRURU1QVCBOTyBMQU5ESU5HIFRIRVJFLg=="
137
    ),
138
    // Terminator
139
    Test(
140
      nullptr,
141
      nullptr
142
    )
143
  };
144
145
class FakeInputStream final : public nsIInputStream
146
{
147
0
  ~FakeInputStream() {}
148
149
public:
150
151
  FakeInputStream()
152
  : mTestNumber(0),
153
    mTest(&kTests[0]),
154
    mChunk(&mTest->mChunks[0]),
155
    mClosed(false)
156
0
  {}
157
158
  NS_DECL_ISUPPORTS
159
  NS_DECL_NSIINPUTSTREAM
160
161
  void Reset();
162
  bool NextTest();
163
  void CheckTest(nsACString& aResult);
164
  void CheckTest(nsAString& aResult);
165
private:
166
  uint32_t mTestNumber;
167
  const Test* mTest;
168
  const Chunk* mChunk;
169
  bool mClosed;
170
};
171
172
NS_IMPL_ISUPPORTS(FakeInputStream, nsIInputStream)
173
174
NS_IMETHODIMP
175
FakeInputStream::Close()
176
0
{
177
0
  mClosed = true;
178
0
  return NS_OK;
179
0
}
180
181
NS_IMETHODIMP
182
FakeInputStream::Available(uint64_t* aAvailable)
183
0
{
184
0
  *aAvailable = 0;
185
0
186
0
  if (mClosed)
187
0
    return NS_BASE_STREAM_CLOSED;
188
0
189
0
  const Chunk* chunk = mChunk;
190
0
  while (chunk->mLength) {
191
0
    *aAvailable += chunk->mLength;
192
0
    chunk++;
193
0
  }
194
0
195
0
  return NS_OK;
196
0
}
197
198
NS_IMETHODIMP
199
FakeInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aOut)
200
0
{
201
0
  return NS_ERROR_NOT_IMPLEMENTED;
202
0
}
203
204
NS_IMETHODIMP
205
FakeInputStream::ReadSegments(nsWriteSegmentFun aWriter,
206
                              void* aClosure,
207
                              uint32_t aCount,
208
                              uint32_t* aRead)
209
0
{
210
0
  *aRead = 0;
211
0
212
0
  if (mClosed)
213
0
    return NS_BASE_STREAM_CLOSED;
214
0
215
0
  while (mChunk->mLength) {
216
0
    uint32_t written = 0;
217
0
218
0
    nsresult rv = (*aWriter)(this, aClosure, mChunk->mData,
219
0
                             *aRead, mChunk->mLength, &written);
220
0
221
0
    *aRead += written;
222
0
    NS_ENSURE_SUCCESS(rv, rv);
223
0
224
0
    mChunk++;
225
0
  }
226
0
227
0
  return NS_OK;
228
0
}
229
230
NS_IMETHODIMP
231
FakeInputStream::IsNonBlocking(bool* aIsBlocking)
232
0
{
233
0
  *aIsBlocking = false;
234
0
  return NS_OK;
235
0
}
236
237
void
238
FakeInputStream::Reset()
239
0
{
240
0
  mClosed = false;
241
0
  mChunk = &mTest->mChunks[0];
242
0
}
243
244
bool
245
FakeInputStream::NextTest()
246
0
{
247
0
  mTestNumber++;
248
0
  mTest = &kTests[mTestNumber];
249
0
  mChunk = &mTest->mChunks[0];
250
0
  mClosed = false;
251
0
252
0
  return mTest->mChunks ? true : false;
253
0
}
254
255
void
256
FakeInputStream::CheckTest(nsACString& aResult)
257
0
{
258
0
  ASSERT_STREQ(aResult.BeginReading(), mTest->mResult);
259
0
}
260
261
void
262
FakeInputStream::CheckTest(nsAString& aResult)
263
0
{
264
0
  ASSERT_TRUE(aResult.EqualsASCII(mTest->mResult)) <<
265
0
    "Actual:   " << aResult.BeginReading() << std::endl <<
266
0
    "Expected: " << mTest->mResult;
267
0
}
268
269
TEST(Base64, StreamEncoder)
270
0
{
271
0
  nsCOMPtr<nsIScriptableBase64Encoder> encoder =
272
0
    do_CreateInstance("@mozilla.org/scriptablebase64encoder;1");
273
0
  ASSERT_TRUE(encoder);
274
0
275
0
  RefPtr<FakeInputStream> stream = new FakeInputStream();
276
0
  do {
277
0
    nsString wideString;
278
0
    nsCString string;
279
0
280
0
    nsresult rv;
281
0
    rv = encoder->EncodeToString(stream, 0, wideString);
282
0
    ASSERT_TRUE(NS_SUCCEEDED(rv));
283
0
284
0
    stream->Reset();
285
0
286
0
    rv = encoder->EncodeToCString(stream, 0, string);
287
0
    ASSERT_TRUE(NS_SUCCEEDED(rv));
288
0
289
0
    stream->CheckTest(wideString);
290
0
    stream->CheckTest(string);
291
0
  } while (stream->NextTest());
292
0
}
293
294
struct EncodeDecodeTestCase
295
{
296
    const char* mInput;
297
    const char* mOutput;
298
};
299
300
static EncodeDecodeTestCase sRFC4648TestCases[] = {
301
   { "", "" },
302
   { "f", "Zg==" },
303
   { "fo", "Zm8=" },
304
   { "foo", "Zm9v" },
305
   { "foob", "Zm9vYg==" },
306
   { "fooba", "Zm9vYmE=" },
307
   { "foobar", "Zm9vYmFy" },
308
};
309
310
TEST(Base64, RFC4648Encoding)
311
0
{
312
0
    for (auto& testcase : sRFC4648TestCases) {
313
0
        nsDependentCString in(testcase.mInput);
314
0
        nsAutoCString out;
315
0
        nsresult rv = mozilla::Base64Encode(in, out);
316
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
317
0
        ASSERT_TRUE(out.EqualsASCII(testcase.mOutput));
318
0
    }
319
0
320
0
    for (auto& testcase : sRFC4648TestCases) {
321
0
        NS_ConvertUTF8toUTF16 in(testcase.mInput);
322
0
        nsAutoString out;
323
0
        nsresult rv = mozilla::Base64Encode(in, out);
324
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
325
0
        ASSERT_TRUE(out.EqualsASCII(testcase.mOutput));
326
0
    }
327
0
}
328
329
TEST(Base64, RFC4648Decoding)
330
0
{
331
0
    for (auto& testcase : sRFC4648TestCases) {
332
0
        nsDependentCString out(testcase.mOutput);
333
0
        nsAutoCString in;
334
0
        nsresult rv = mozilla::Base64Decode(out, in);
335
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
336
0
        ASSERT_TRUE(in.EqualsASCII(testcase.mInput));
337
0
    }
338
0
339
0
    for (auto& testcase : sRFC4648TestCases) {
340
0
        NS_ConvertUTF8toUTF16 out(testcase.mOutput);
341
0
        nsAutoString in;
342
0
        nsresult rv = mozilla::Base64Decode(out, in);
343
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
344
0
        ASSERT_TRUE(in.EqualsASCII(testcase.mInput));
345
0
    }
346
0
}
347
348
TEST(Base64, RFC4648DecodingRawPointers)
349
0
{
350
0
    for (auto& testcase : sRFC4648TestCases) {
351
0
        size_t outputLength = strlen(testcase.mOutput);
352
0
        size_t inputLength = strlen(testcase.mInput);
353
0
354
0
        // This will be allocated by Base64Decode.
355
0
        char* buffer = nullptr;
356
0
357
0
        uint32_t binaryLength = 0;
358
0
        nsresult rv = mozilla::Base64Decode(testcase.mOutput, outputLength,
359
0
                                            &buffer, &binaryLength);
360
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
361
0
        ASSERT_EQ(binaryLength, inputLength);
362
0
        ASSERT_STREQ(testcase.mInput, buffer);
363
0
        free(buffer);
364
0
    }
365
0
}
366
367
static EncodeDecodeTestCase sNonASCIITestCases[] = {
368
    { "\x80", "gA==" },
369
    { "\xff", "/w==" },
370
    { "\x80\x80", "gIA=" },
371
    { "\x80\x81", "gIE=" },
372
    { "\xff\xff", "//8=" },
373
    { "\x80\x80\x80", "gICA" },
374
    { "\xff\xff\xff", "////" },
375
    { "\x80\x80\x80\x80", "gICAgA==" },
376
    { "\xff\xff\xff\xff", "/////w==" },
377
};
378
379
TEST(Base64, NonASCIIEncoding)
380
0
{
381
0
    for (auto& testcase : sNonASCIITestCases) {
382
0
        nsDependentCString in(testcase.mInput);
383
0
        nsAutoCString out;
384
0
        nsresult rv = mozilla::Base64Encode(in, out);
385
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
386
0
        ASSERT_TRUE(out.EqualsASCII(testcase.mOutput));
387
0
    }
388
0
}
389
390
TEST(Base64, NonASCIIEncodingWideString)
391
0
{
392
0
    for (auto& testcase : sNonASCIITestCases) {
393
0
        nsAutoString in, out;
394
0
        // XXX Handles Latin1 despite the name
395
0
        AppendASCIItoUTF16(nsDependentCString(testcase.mInput), in);
396
0
        nsresult rv = mozilla::Base64Encode(in, out);
397
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
398
0
        ASSERT_TRUE(out.EqualsASCII(testcase.mOutput));
399
0
    }
400
0
}
401
402
TEST(Base64, NonASCIIDecoding)
403
0
{
404
0
    for (auto& testcase : sNonASCIITestCases) {
405
0
        nsDependentCString out(testcase.mOutput);
406
0
        nsAutoCString in;
407
0
        nsresult rv = mozilla::Base64Decode(out, in);
408
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
409
0
        ASSERT_TRUE(in.Equals(testcase.mInput));
410
0
    }
411
0
}
412
413
TEST(Base64, NonASCIIDecodingWideString)
414
0
{
415
0
    for (auto& testcase : sNonASCIITestCases) {
416
0
        nsAutoString in, out;
417
0
        // XXX Handles Latin1 despite the name
418
0
        AppendASCIItoUTF16(nsDependentCString(testcase.mOutput), out);
419
0
        nsresult rv = mozilla::Base64Decode(out, in);
420
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
421
0
        // Can't use EqualsASCII, because our comparison string isn't ASCII.
422
0
        for (size_t i = 0; i < in.Length(); ++i) {
423
0
            ASSERT_TRUE(((unsigned int)in[i] & 0xff00) == 0);
424
0
            ASSERT_EQ((unsigned char)in[i], (unsigned char)testcase.mInput[i]);
425
0
        }
426
0
        ASSERT_TRUE(strlen(testcase.mInput) == in.Length());
427
0
    }
428
0
}
429
430
// For historical reasons, our wide string base64 encode routines mask off
431
// the high bits of non-latin1 wide strings.
432
TEST(Base64, EncodeNon8BitWideString)
433
0
{
434
0
    {
435
0
        const nsAutoString non8Bit(u"\x1ff");
436
0
        nsAutoString out;
437
0
        nsresult rv = mozilla::Base64Encode(non8Bit, out);
438
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
439
0
        ASSERT_TRUE(out.EqualsLiteral("/w=="));
440
0
    }
441
0
    {
442
0
        const nsAutoString non8Bit(u"\xfff");
443
0
        nsAutoString out;
444
0
        nsresult rv = mozilla::Base64Encode(non8Bit, out);
445
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
446
0
        ASSERT_TRUE(out.EqualsLiteral("/w=="));
447
0
    }
448
0
}
449
450
// For historical reasons, our wide string base64 decode routines mask off
451
// the high bits of non-latin1 wide strings.
452
TEST(Base64, DecodeNon8BitWideString)
453
0
{
454
0
    {
455
0
        // This would be "/w==" in a nsCString
456
0
        const nsAutoString non8Bit(u"\x12f\x177==");
457
0
        const nsAutoString expectedOutput(u"\xff");
458
0
        ASSERT_EQ(non8Bit.Length(), 4u);
459
0
        nsAutoString out;
460
0
        nsresult rv = mozilla::Base64Decode(non8Bit, out);
461
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
462
0
        ASSERT_TRUE(out.Equals(expectedOutput));
463
0
    }
464
0
    {
465
0
        const nsAutoString non8Bit(u"\xf2f\xf77==");
466
0
        const nsAutoString expectedOutput(u"\xff");
467
0
        nsAutoString out;
468
0
        nsresult rv = mozilla::Base64Decode(non8Bit, out);
469
0
        ASSERT_TRUE(NS_SUCCEEDED(rv));
470
0
        ASSERT_TRUE(out.Equals(expectedOutput));
471
0
    }
472
0
}
473
474
TEST(Base64, TruncateOnInvalidDecodeCString)
475
0
{
476
0
    NS_NAMED_LITERAL_CSTRING(invalid, "@@==");
477
0
    nsAutoCString out("I should be truncated!");
478
0
    nsresult rv = mozilla::Base64Decode(invalid, out);
479
0
    ASSERT_TRUE(NS_FAILED(rv));
480
0
    ASSERT_EQ(out.Length(), 0u);
481
0
}
482
483
TEST(Base64, TruncateOnInvalidDecodeWideString)
484
0
{
485
0
    NS_NAMED_LITERAL_STRING(invalid, "@@==");
486
0
    nsAutoString out(u"I should be truncated!");
487
0
    nsresult rv = mozilla::Base64Decode(invalid, out);
488
0
    ASSERT_TRUE(NS_FAILED(rv));
489
0
    ASSERT_EQ(out.Length(), 0u);
490
0
}
491
492
// TODO: Add tests for OOM handling.