com.android.sdklib.internal.repository.DownloadCacheTest.java Source code

Java tutorial

Introduction

Here is the source code for com.android.sdklib.internal.repository.DownloadCacheTest.java

Source

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.sdklib.internal.repository;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.sdklib.AndroidLocationTestCase;
import com.android.sdklib.internal.repository.DownloadCache.Strategy;
import com.android.sdklib.io.FileOp;
import com.android.sdklib.io.IFileOp;
import com.android.sdklib.io.MockFileOp;
import com.android.utils.Pair;
import com.google.common.base.Charsets;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class DownloadCacheTest extends AndroidLocationTestCase {

    private MockFileOp mFileOp;
    private MockMonitor mMonitor;

    /**
     * A private version of DownloadCache that never calls {@link UrlOpener}.
     */
    private static class NoDownloadCache extends DownloadCache {

        private final Map<String, Pair<InputStream, HttpResponse>> mReplies = new HashMap<String, Pair<InputStream, HttpResponse>>();

        public NoDownloadCache(@NonNull Strategy strategy) {
            super(strategy);
        }

        public NoDownloadCache(@NonNull IFileOp fileOp, @NonNull Strategy strategy) {
            super(fileOp, strategy);
        }

        @Override
        protected Pair<InputStream, HttpResponse> openUrl(@NonNull String url, boolean needsMarkResetSupport,
                @NonNull ITaskMonitor monitor, @Nullable Header[] headers)
                throws IOException, CanceledByUserException {

            Pair<InputStream, HttpResponse> reply = mReplies.get(url);
            if (reply != null) {
                return reply;
            }

            // http-client's behavior is to return a FNF instead of 404.
            throw new FileNotFoundException(url);
        }

        public void registerResponse(@NonNull String url, int httpCode, @Nullable String content) {
            InputStream is = null;
            if (content != null) {
                is = new ByteArrayInputStream(content.getBytes(Charsets.UTF_8));
            }

            ProtocolVersion p = new ProtocolVersion("HTTP", 1, 1);
            StatusLine statusLine = new BasicStatusLine(p, httpCode, "Code " + httpCode);
            HttpResponse httpResponse = new BasicHttpResponse(statusLine);
            Pair<InputStream, HttpResponse> reply = Pair.of(is, httpResponse);

            mReplies.put(url, reply);
        }

    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mFileOp = new MockFileOp();
        mMonitor = new MockMonitor();
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
    }

    public void testMissingResource() throws Exception {
        // Downloads must fail when using the only-cache strategy and there's nothing in the cache.
        // In that case, it returns null to indicate the resource is simply not found.
        // Since the mock implementation always returns a 404 and no stream, there is no
        // difference between the various cache strategies.

        mFileOp.reset();
        NoDownloadCache d1 = new NoDownloadCache(mFileOp, Strategy.ONLY_CACHE);
        InputStream is1 = d1.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        assertNull(is1);
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertTrue(mFileOp.hasRecordedExistingFolder(d1.getCacheRoot()));
        assertEquals("[]", Arrays.toString(mFileOp.getOutputStreams()));

        // HTTP-Client's behavior is to return a FNF instead of 404 so we'll try that first
        mFileOp.reset();
        NoDownloadCache d2 = new NoDownloadCache(mFileOp, Strategy.DIRECT);

        try {
            d2.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
            fail("Expected: NoDownloadCache.openCachedUrl should have thrown a FileNotFoundException");
        } catch (FileNotFoundException e) {
            assertEquals("http://www.example.com/download1.xml", e.getMessage());
        }
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertEquals("[]", Arrays.toString(mFileOp.getOutputStreams()));

        // Try again but this time we'll define a 404 reply to test the rest of the code path.
        mFileOp.reset();
        d2.registerResponse("http://www.example.com/download1.xml", 404, null);
        InputStream is2 = d2.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        assertNull(is2);
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertEquals("[]", Arrays.toString(mFileOp.getOutputStreams()));

        mFileOp.reset();
        NoDownloadCache d3 = new NoDownloadCache(mFileOp, Strategy.SERVE_CACHE);
        d3.registerResponse("http://www.example.com/download1.xml", 404, null);
        InputStream is3 = d3.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        assertNull(is3);
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertEquals("[]", Arrays.toString(mFileOp.getOutputStreams()));

        mFileOp.reset();
        NoDownloadCache d4 = new NoDownloadCache(mFileOp, Strategy.FRESH_CACHE);
        d4.registerResponse("http://www.example.com/download1.xml", 404, null);
        InputStream is4 = d4.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        assertNull(is4);
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertEquals("[]", Arrays.toString(mFileOp.getOutputStreams()));
    }

    public void testExistingResource() throws Exception {
        // The resource exists but only-cache doesn't hit the network so it will
        // fail when the resource is not cached.
        mFileOp.reset();
        NoDownloadCache d1 = new NoDownloadCache(mFileOp, Strategy.ONLY_CACHE);
        d1.registerResponse("http://www.example.com/download1.xml", 200, "Blah blah blah");
        InputStream is1 = d1.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        assertNull(is1);
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertTrue(mFileOp.hasRecordedExistingFolder(d1.getCacheRoot()));
        assertEquals("[]", Arrays.toString(mFileOp.getOutputStreams()));

        // HTTP-Client's behavior is to return a FNF instead of 404 so we'll try that first
        mFileOp.reset();
        NoDownloadCache d2 = new NoDownloadCache(mFileOp, Strategy.DIRECT);
        d2.registerResponse("http://www.example.com/download1.xml", 200, "Blah blah blah");
        InputStream is2 = d2.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        assertNotNull(is2);
        assertEquals("Blah blah blah", new BufferedReader(new InputStreamReader(is2, Charsets.UTF_8)).readLine());
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertEquals("[]", Arrays.toString(mFileOp.getOutputStreams()));

        mFileOp.reset();
        NoDownloadCache d3 = new NoDownloadCache(mFileOp, Strategy.SERVE_CACHE);
        d3.registerResponse("http://www.example.com/download1.xml", 200, "Blah blah blah");
        InputStream is3 = d3.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        assertNotNull(is3);
        assertEquals("Blah blah blah", new BufferedReader(new InputStreamReader(is3, Charsets.UTF_8)).readLine());
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertEquals("[<$CACHE/sdkbin-1_9b8dc757-download1_xml: 'Blah blah blah'>, "
                + "<$CACHE/sdkinf-1_9b8dc757-download1_xml: '### Meta data for SDK Manager cache. Do not modify.\n"
                + "#<creation timestamp>\n" + "URL=http\\://www.example.com/download1.xml\n" + "Status-Code=200\n"
                + "'>]", sanitize(d3, Arrays.toString(mFileOp.getOutputStreams())));

        mFileOp.reset();
        NoDownloadCache d4 = new NoDownloadCache(mFileOp, Strategy.FRESH_CACHE);
        d4.registerResponse("http://www.example.com/download1.xml", 200, "Blah blah blah");
        InputStream is4 = d4.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        assertNotNull(is4);
        assertEquals("Blah blah blah", new BufferedReader(new InputStreamReader(is4, Charsets.UTF_8)).readLine());
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertEquals("[<$CACHE/sdkbin-1_9b8dc757-download1_xml: 'Blah blah blah'>, "
                + "<$CACHE/sdkinf-1_9b8dc757-download1_xml: '### Meta data for SDK Manager cache. Do not modify.\n"
                + "#<creation timestamp>\n" + "URL=http\\://www.example.com/download1.xml\n" + "Status-Code=200\n"
                + "'>]", sanitize(d4, Arrays.toString(mFileOp.getOutputStreams())));
    }

    public void testCachedResource() throws Exception {
        mFileOp.reset();
        NoDownloadCache d1 = new NoDownloadCache(mFileOp, Strategy.ONLY_CACHE);
        d1.registerResponse("http://www.example.com/download1.xml", 200, "This is the new content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d1.getCacheRoot(), "sdkbin-1_9b8dc757-download1_xml")),
                123456L, "This is the cached content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d1.getCacheRoot(), "sdkinf-1_9b8dc757-download1_xml")),
                123456L, "URL=http\\://www.example.com/download1.xml\n" + "Status-Code=200\n");
        InputStream is1 = d1.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        // Only-cache strategy returns the value from the cache, not the actual resource.
        assertEquals("This is the cached content",
                new BufferedReader(new InputStreamReader(is1, Charsets.UTF_8)).readLine());
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertTrue(mFileOp.hasRecordedExistingFolder(d1.getCacheRoot()));
        // The cache hasn't been modified, only read
        assertEquals("[]", sanitize(d1, Arrays.toString(mFileOp.getOutputStreams())));

        // Direct ignores the cache.
        mFileOp.reset();
        NoDownloadCache d2 = new NoDownloadCache(mFileOp, Strategy.DIRECT);
        d2.registerResponse("http://www.example.com/download1.xml", 200, "This is the new content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d2.getCacheRoot(), "sdkbin-1_9b8dc757-download1_xml")),
                123456L, "This is the cached content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d2.getCacheRoot(), "sdkinf-1_9b8dc757-download1_xml")),
                123456L, "URL=http\\://www.example.com/download1.xml\n" + "Status-Code=200\n");
        InputStream is2 = d2.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        // Direct strategy ignores the cache.
        assertEquals("This is the new content",
                new BufferedReader(new InputStreamReader(is2, Charsets.UTF_8)).readLine());
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertTrue(mFileOp.hasRecordedExistingFolder(d2.getCacheRoot()));
        // Direct strategy doesn't update the cache.
        assertEquals("[]", sanitize(d2, Arrays.toString(mFileOp.getOutputStreams())));

        // Serve-cache reads from the cache if available, ignoring its freshness (here the timestamp
        // is way older than the 10-minute freshness encoded in the DownloadCache.)
        mFileOp.reset();
        NoDownloadCache d3 = new NoDownloadCache(mFileOp, Strategy.SERVE_CACHE);
        d3.registerResponse("http://www.example.com/download1.xml", 200, "This is the new content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d3.getCacheRoot(), "sdkbin-1_9b8dc757-download1_xml")),
                123456L, "This is the cached content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d3.getCacheRoot(), "sdkinf-1_9b8dc757-download1_xml")),
                123456L, "URL=http\\://www.example.com/download1.xml\n" + "Status-Code=200\n");
        InputStream is3 = d3.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        // We get content from the cache.
        assertEquals("This is the cached content",
                new BufferedReader(new InputStreamReader(is3, Charsets.UTF_8)).readLine());
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertTrue(mFileOp.hasRecordedExistingFolder(d3.getCacheRoot()));
        // Cache isn't updated since nothing fresh was read.
        assertEquals("[]", sanitize(d3, Arrays.toString(mFileOp.getOutputStreams())));

        // fresh-cache reads the cache, finds it stale (here the timestamp
        // is way older than the 10-minute freshness encoded in the DownloadCache)
        // and will fetch the new resource instead and update the cache.
        mFileOp.reset();
        NoDownloadCache d4 = new NoDownloadCache(mFileOp, Strategy.FRESH_CACHE);
        d4.registerResponse("http://www.example.com/download1.xml", 200, "This is the new content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d4.getCacheRoot(), "sdkbin-1_9b8dc757-download1_xml")),
                123456L, "This is the cached content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d4.getCacheRoot(), "sdkinf-1_9b8dc757-download1_xml")),
                123456L, "URL=http\\://www.example.com/download1.xml\n" + "Status-Code=200\n");
        InputStream is4 = d4.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        // Cache is discarded, actual resource is returned.
        assertEquals("This is the new content",
                new BufferedReader(new InputStreamReader(is4, Charsets.UTF_8)).readLine());
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertTrue(mFileOp.hasRecordedExistingFolder(d4.getCacheRoot()));
        // Cache isn updated since something fresh was read.
        assertEquals("[<$CACHE/sdkbin-1_9b8dc757-download1_xml: 'This is the new content'>, "
                + "<$CACHE/sdkinf-1_9b8dc757-download1_xml: '### Meta data for SDK Manager cache. Do not modify.\n"
                + "#<creation timestamp>\n" + "URL=http\\://www.example.com/download1.xml\n" + "Status-Code=200\n"
                + "'>]", sanitize(d4, Arrays.toString(mFileOp.getOutputStreams())));

        // fresh-cache reads the cache, finds it still valid stale (less than 10-minute old),
        // and uses the cached resource.
        mFileOp.reset();
        NoDownloadCache d5 = new NoDownloadCache(mFileOp, Strategy.FRESH_CACHE);
        d5.registerResponse("http://www.example.com/download1.xml", 200, "This is the new content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d5.getCacheRoot(), "sdkbin-1_9b8dc757-download1_xml")),
                System.currentTimeMillis() - 1000, "This is the cached content");
        mFileOp.recordExistingFile(
                mFileOp.getAgnosticAbsPath(FileOp.append(d5.getCacheRoot(), "sdkinf-1_9b8dc757-download1_xml")),
                System.currentTimeMillis() - 1000,
                "URL=http\\://www.example.com/download1.xml\n" + "Status-Code=200\n");
        InputStream is5 = d5.openCachedUrl("http://www.example.com/download1.xml", mMonitor);
        // Cache is used.
        assertEquals("This is the cached content",
                new BufferedReader(new InputStreamReader(is5, Charsets.UTF_8)).readLine());
        assertEquals("", mMonitor.getAllCapturedLogs());
        assertTrue(mFileOp.hasRecordedExistingFolder(d5.getCacheRoot()));
        // Cache isn't updated since nothing fresh was read.
        assertEquals("[]", sanitize(d5, Arrays.toString(mFileOp.getOutputStreams())));
    }

    // --------

    @Nullable
    private String sanitize(@NonNull DownloadCache dc, @Nullable String msg) {
        if (msg != null) {
            msg = msg.replace("\r\n", "\n");

            String absRoot = mFileOp.getAgnosticAbsPath(dc.getCacheRoot());
            msg = msg.replace(absRoot, "$CACHE");

            // Cached files also contain a creation timestamp which we need to find and remove.
            msg = msg.replaceAll("\n#[A-Z][A-Za-z0-9: ]+20[0-9]{2}\n", "\n#<creation timestamp>\n");
        }
        return msg;
    }

}