13713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick/*
23713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Copyright (C) 2011 The Android Open Source Project
33713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick *
43713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Licensed under the Apache License, Version 2.0 (the "License");
53713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * you may not use this file except in compliance with the License.
63713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * You may obtain a copy of the License at
73713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick *
83713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick *      http://www.apache.org/licenses/LICENSE-2.0
93713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick *
103713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Unless required by applicable law or agreed to in writing, software
113713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * distributed under the License is distributed on an "AS IS" BASIS,
123713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * See the License for the specific language governing permissions and
143713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * limitations under the License.
153713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */
163713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
173713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickpackage com.android.volley;
183713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
193713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.Request.Priority;
203713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.mock.MockNetwork;
213713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.mock.MockRequest;
223713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.toolbox.NoCache;
233713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.utils.CacheTestUtils;
243713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.utils.ImmediateResponseDelivery;
253713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
263713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport android.os.SystemClock;
273713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport android.test.InstrumentationTestCase;
283713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport android.test.UiThreadTest;
29cebf98c8edef6b3506258ddb68266febadd0db1aJeff Sharkeyimport android.test.suitebuilder.annotation.LargeTest;
303713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
313713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.ArrayList;
323713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.List;
333713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.Random;
343713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.concurrent.Semaphore;
353713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.concurrent.TimeUnit;
363713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport java.util.concurrent.atomic.AtomicInteger;
373713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
38cebf98c8edef6b3506258ddb68266febadd0db1aJeff Sharkey@LargeTest
393713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickpublic class RequestQueueTest extends InstrumentationTestCase {
403713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private ResponseDelivery mDelivery;
413713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    @Override
433713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    protected void setUp() throws Exception {
443713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        super.setUp();
453713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
463713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        mDelivery = new ImmediateResponseDelivery();
473713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
483713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
493713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /**
503713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * Make a list of requests with random priorities.
513713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param count Number of requests to make
523713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     */
533713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private List<MockRequest> makeRequests(int count) {
543713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        Request.Priority[] allPriorities = Request.Priority.values();
553713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        Random random = new Random();
563713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
573713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        List<MockRequest> requests = new ArrayList<MockRequest>();
583713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        for (int i = 0; i < count; i++) {
593713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            MockRequest request = new MockRequest();
603713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            Request.Priority priority = allPriorities[random.nextInt(allPriorities.length)];
613713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            request.setCacheKey(String.valueOf(i));
623713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            request.setPriority(priority);
633713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            requests.add(request);
643713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
653713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        return requests;
663713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
673713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
683713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    @UiThreadTest
693713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    public void testAdd_requestProcessedInCorrectOrder() throws Exception {
703713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        int requestsToMake = 100;
713713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
723713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        OrderCheckingNetwork network = new OrderCheckingNetwork();
733713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        RequestQueue queue = new RequestQueue(new NoCache(), network, 1, mDelivery);
743713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
753713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        for (Request<?> request : makeRequests(requestsToMake)) {
763713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            queue.add(request);
773713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
783713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
793713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        network.setExpectedRequests(requestsToMake);
803713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        queue.start();
813713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        network.waitUntilExpectedDone(2000); // 2 seconds
823713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        queue.stop();
833713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
843713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
853713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    public void testAdd_dedupeByCacheKey() throws Exception {
863713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        OrderCheckingNetwork network = new OrderCheckingNetwork();
873713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        final AtomicInteger parsed = new AtomicInteger();
883713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        final AtomicInteger delivered = new AtomicInteger();
893713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        // Enqueue 2 requests with the same cache key. The first request takes 1.5s. Assert that the
903713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        // second request is only handled after the first one has been parsed and delivered.
913713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        DelayedRequest req1 = new DelayedRequest(1500, parsed, delivered);
923713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        DelayedRequest req2 = new DelayedRequest(0, parsed, delivered) {
933713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            @Override
943713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            protected Response<Object> parseNetworkResponse(NetworkResponse response) {
953713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                assertEquals(1, parsed.get());  // req1 must have been parsed.
963713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                assertEquals(1, delivered.get());  // req1 must have been parsed.
973713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                return super.parseNetworkResponse(response);
983713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            }
993713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        };
1003713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        network.setExpectedRequests(2);
1013713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        RequestQueue queue = new RequestQueue(new NoCache(), network, 3, mDelivery);
1023713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        queue.add(req1);
1033713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        queue.add(req2);
1043713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        queue.start();
1053713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        network.waitUntilExpectedDone(2000);
1063713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        queue.stop();
1073713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
1083713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
10919a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick    public void testCancelAll_onlyCorrectTag() throws Exception {
11019a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        MockNetwork network = new MockNetwork();
11119a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        RequestQueue queue = new RequestQueue(new NoCache(), network, 3, mDelivery);
11219a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        Object tagA = new Object();
11319a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        Object tagB = new Object();
11419a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        MockRequest req1 = new MockRequest();
11519a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        req1.setTag(tagA);
11619a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        MockRequest req2 = new MockRequest();
11719a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        req2.setTag(tagB);
11819a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        MockRequest req3 = new MockRequest();
11919a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        req3.setTag(tagA);
12019a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        MockRequest req4 = new MockRequest();
12119a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        req4.setTag(tagA);
12219a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick
12319a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        queue.add(req1); // A
12419a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        queue.add(req2); // B
12519a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        queue.add(req3); // A
12619a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        queue.cancelAll(tagA);
12719a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        queue.add(req4); // A
12819a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick
12919a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        assertTrue(req1.cancel_called); // A cancelled
13019a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        assertFalse(req2.cancel_called); // B not cancelled
13119a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        assertTrue(req3.cancel_called); // A cancelled
13219a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick        assertFalse(req4.cancel_called); // A added after cancel not cancelled
13319a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick    }
13419a56e8495477bfd89e4c4913860fdbcb7beb504Ficus Kirkpatrick
1353713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private class OrderCheckingNetwork implements Network {
1363713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        private Priority mLastPriority = Priority.IMMEDIATE;
1373713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        private int mLastSequence = -1;
1383713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        private Semaphore mSemaphore;
1393713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1403713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        public void setExpectedRequests(int expectedRequests) {
1413713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            // Leave one permit available so the waiter can find it.
1423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            expectedRequests--;
1433713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mSemaphore = new Semaphore(-expectedRequests);
1443713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1453713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1463713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        public void waitUntilExpectedDone(long timeout)
1473713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                throws InterruptedException, TimeoutError {
1483713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            if (mSemaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS) == false) {
1493713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                throw new TimeoutError();
1503713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            }
1513713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1523713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1533713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        @Override
1543713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        public NetworkResponse performRequest(Request<?> request) {
1553713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            Priority thisPriority = request.getPriority();
1563713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            int thisSequence = request.getSequence();
1573713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1583713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            int priorityDiff = thisPriority.compareTo(mLastPriority);
1593713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1603713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            // Should never experience a higher priority after a lower priority
1613713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            assertFalse(priorityDiff > 0);
1623713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1633713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            // If we're not transitioning to a new priority block, check sequence numbers
1643713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            if (priorityDiff == 0) {
1653713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                assertTrue(thisSequence > mLastSequence);
1663713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            }
1673713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mLastSequence = thisSequence;
1683713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mLastPriority = thisPriority;
1693713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1703713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mSemaphore.release();
1713713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            return new NetworkResponse(new byte[16]);
1723713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1733713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
1743713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1753713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private class DelayedRequest extends Request<Object> {
1763713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        private final long mDelayMillis;
1773713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        private final AtomicInteger mParsedCount;
1783713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        private final AtomicInteger mDeliveredCount;
1793713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1803713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        public DelayedRequest(long delayMillis, AtomicInteger parsed, AtomicInteger delivered) {
1813713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            super("http://buganizer/", null);
1823713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mDelayMillis = delayMillis;
1833713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mParsedCount = parsed;
1843713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mDeliveredCount = delivered;
1853713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1863713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1873713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        @Override
1883713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        protected Response<Object> parseNetworkResponse(NetworkResponse response) {
1893713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mParsedCount.incrementAndGet();
1903713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            SystemClock.sleep(mDelayMillis);
1913713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            return Response.success(new Object(), CacheTestUtils.makeRandomCacheEntry(null));
1923713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1933713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1943713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        @Override
1953713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        protected void deliverResponse(Object response) {
1963713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            mDeliveredCount.incrementAndGet();
1973713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1983713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
1993713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
2003713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick}
201