chromium/android_webview/javatests/src/org/chromium/android_webview/test/common/variations/VariationsUtilsTest.java

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.android_webview.test.common.variations;

import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.EITHER_PROCESS;

import androidx.test.filters.MediumTest;

import com.google.protobuf.ByteString;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.android_webview.common.variations.VariationsUtils;
import org.chromium.android_webview.proto.AwVariationsSeedOuterClass.AwVariationsSeed;
import org.chromium.android_webview.test.AwJUnit4ClassRunner;
import org.chromium.android_webview.test.OnlyRunIn;
import org.chromium.android_webview.test.util.VariationsTestUtils;
import org.chromium.base.test.util.Batch;
import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

/** Test reading and writing variations seeds. */
@RunWith(AwJUnit4ClassRunner.class)
@OnlyRunIn(EITHER_PROCESS) // These are unit tests
@Batch(Batch.UNIT_TESTS)
public class VariationsUtilsTest {
    @Test
    @MediumTest
    public void testWriteAndReadSeed() throws IOException {
        File file = null;
        try {
            file = File.createTempFile("seed", null, null);
            VariationsTestUtils.writeMockSeed(file);
            SeedInfo readSeed = VariationsUtils.readSeedFile(file);
            VariationsTestUtils.assertSeedsEqual(VariationsTestUtils.createMockSeed(), readSeed);
        } finally {
            if (file != null) file.delete();
        }
    }

    // Test reading a seed that has some but not all fields, which should fail.
    @Test
    @MediumTest
    public void testReadSeedMissingFields() throws IOException {
        File file = null;
        try {
            file = File.createTempFile("seed", null, null);
            FileOutputStream stream = null;
            try {
                // Create a seed that's missing some fields.
                stream = new FileOutputStream(file);
                SeedInfo info = VariationsTestUtils.createMockSeed();
                AwVariationsSeed proto =
                        AwVariationsSeed.newBuilder()
                                .setSignature(info.signature)
                                .setCountry(info.country)
                                .setDate(info.date)
                                .build();
                proto.writeTo(stream);

                Assert.assertNull(
                        "Seed with missing fields should've failed to load.",
                        VariationsUtils.readSeedFile(file));
            } finally {
                if (stream != null) stream.close();
            }
        } finally {
            if (file != null) file.delete();
        }
    }

    // Test reading a seed that's been truncated at some arbitrary byte offsets, which should fail.
    @Test
    @MediumTest
    public void testReadTruncatedSeed() throws IOException {
        // Create a complete, serialized seed.
        SeedInfo info = VariationsTestUtils.createMockSeed();
        AwVariationsSeed proto =
                AwVariationsSeed.newBuilder()
                        .setSignature(info.signature)
                        .setCountry(info.country)
                        .setDate(info.date)
                        .setIsGzipCompressed(info.isGzipCompressed)
                        .setSeedData(ByteString.copyFrom(info.seedData))
                        .build();
        byte[] protoBytes = proto.toByteArray();

        // Sanity check: protoBytes is at least as long as the seedData field.
        Assert.assertTrue(protoBytes.length >= info.seedData.length);

        // Create slices of that seed in 10-byte increments.
        for (int offset = 10; offset < protoBytes.length; offset += 10) {
            byte[] slice = Arrays.copyOfRange(protoBytes, 0, offset);
            File file = null;
            try {
                file = File.createTempFile("seed", null, null);
                FileOutputStream stream = null;
                try {
                    stream = new FileOutputStream(file);
                    stream.write(slice);
                } finally {
                    if (stream != null) stream.close();
                }

                // Reading each truncated seed should fail.
                Assert.assertNull(
                        "Seed truncated from "
                                + protoBytes.length
                                + " to "
                                + offset
                                + " bytes should've failed to load.",
                        VariationsUtils.readSeedFile(file));
            } finally {
                if (file != null) file.delete();
            }
        }
    }
}