Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(417)

Side by Side Diff: components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java

Issue 2292113002: Fix CronetUrlRequestTest#testFailures flake (Closed)
Patch Set: missed one Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.net; 5 package org.chromium.net;
6 6
7 import android.os.ConditionVariable; 7 import android.os.ConditionVariable;
8 import android.os.StrictMode; 8 import android.os.StrictMode;
9 import android.test.MoreAsserts; 9 import android.test.MoreAsserts;
10 import android.test.suitebuilder.annotation.SmallTest; 10 import android.test.suitebuilder.annotation.SmallTest;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
56 } 56 }
57 57
58 private TestUrlRequestCallback startAndWaitForComplete(String url) throws Ex ception { 58 private TestUrlRequestCallback startAndWaitForComplete(String url) throws Ex ception {
59 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 59 TestUrlRequestCallback callback = new TestUrlRequestCallback();
60 // Create request. 60 // Create request.
61 UrlRequest.Builder builder = new UrlRequest.Builder( 61 UrlRequest.Builder builder = new UrlRequest.Builder(
62 url, callback, callback.getExecutor(), mTestFramework.mCronetEng ine); 62 url, callback, callback.getExecutor(), mTestFramework.mCronetEng ine);
63 UrlRequest urlRequest = builder.build(); 63 UrlRequest urlRequest = builder.build();
64 urlRequest.start(); 64 urlRequest.start();
65 callback.blockForDone(); 65 callback.blockForDone();
66 // Wait for all posted tasks to be executed to ensure there is no unhand led exception.
67 callback.shutdownExecutorAndWait();
66 assertTrue(urlRequest.isDone()); 68 assertTrue(urlRequest.isDone());
67 return callback; 69 return callback;
68 } 70 }
69 71
70 private void checkResponseInfo(UrlResponseInfo responseInfo, String expected Url, 72 private void checkResponseInfo(UrlResponseInfo responseInfo, String expected Url,
71 int expectedHttpStatusCode, String expectedHttpStatusText) { 73 int expectedHttpStatusCode, String expectedHttpStatusText) {
72 assertEquals(expectedUrl, responseInfo.getUrl()); 74 assertEquals(expectedUrl, responseInfo.getUrl());
73 assertEquals( 75 assertEquals(
74 expectedUrl, responseInfo.getUrlChain().get(responseInfo.getUrlC hain().size() - 1)); 76 expectedUrl, responseInfo.getUrlChain().get(responseInfo.getUrlC hain().size() - 1));
75 assertEquals(expectedHttpStatusCode, responseInfo.getHttpStatusCode()); 77 assertEquals(expectedHttpStatusCode, responseInfo.getHttpStatusCode());
(...skipping 544 matching lines...) Expand 10 before | Expand all | Expand 10 after
620 public void testMockStartAsyncError() throws Exception { 622 public void testMockStartAsyncError() throws Exception {
621 final int arbitraryNetError = -3; 623 final int arbitraryNetError = -3;
622 TestUrlRequestCallback callback = 624 TestUrlRequestCallback callback =
623 startAndWaitForComplete(MockUrlRequestJobFactory.getMockUrlWithF ailure( 625 startAndWaitForComplete(MockUrlRequestJobFactory.getMockUrlWithF ailure(
624 FailurePhase.START, arbitraryNetError)); 626 FailurePhase.START, arbitraryNetError));
625 assertNull(callback.mResponseInfo); 627 assertNull(callback.mResponseInfo);
626 assertNotNull(callback.mError); 628 assertNotNull(callback.mError);
627 assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCo de()); 629 assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCo de());
628 assertEquals(0, callback.mRedirectCount); 630 assertEquals(0, callback.mRedirectCount);
629 assertTrue(callback.mOnErrorCalled); 631 assertTrue(callback.mOnErrorCalled);
630 assertEquals(callback.mResponseStep, ResponseStep.NOTHING); 632 assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
631 } 633 }
632 634
633 @SmallTest 635 @SmallTest
634 @Feature({"Cronet"}) 636 @Feature({"Cronet"})
635 @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory 637 @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
636 public void testMockReadDataSyncError() throws Exception { 638 public void testMockReadDataSyncError() throws Exception {
637 final int arbitraryNetError = -4; 639 final int arbitraryNetError = -4;
638 TestUrlRequestCallback callback = 640 TestUrlRequestCallback callback =
639 startAndWaitForComplete(MockUrlRequestJobFactory.getMockUrlWithF ailure( 641 startAndWaitForComplete(MockUrlRequestJobFactory.getMockUrlWithF ailure(
640 FailurePhase.READ_SYNC, arbitraryNetError)); 642 FailurePhase.READ_SYNC, arbitraryNetError));
641 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); 643 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
642 assertEquals(0, callback.mResponseInfo.getReceivedBytesCount()); 644 assertEquals(0, callback.mResponseInfo.getReceivedBytesCount());
643 assertNotNull(callback.mError); 645 assertNotNull(callback.mError);
644 assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCo de()); 646 assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCo de());
645 assertEquals(0, callback.mRedirectCount); 647 assertEquals(0, callback.mRedirectCount);
646 assertTrue(callback.mOnErrorCalled); 648 assertTrue(callback.mOnErrorCalled);
647 assertEquals(callback.mResponseStep, ResponseStep.ON_RESPONSE_STARTED); 649 assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
648 } 650 }
649 651
650 @SmallTest 652 @SmallTest
651 @Feature({"Cronet"}) 653 @Feature({"Cronet"})
652 @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory 654 @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
653 public void testMockReadDataAsyncError() throws Exception { 655 public void testMockReadDataAsyncError() throws Exception {
654 final int arbitraryNetError = -5; 656 final int arbitraryNetError = -5;
655 TestUrlRequestCallback callback = 657 TestUrlRequestCallback callback =
656 startAndWaitForComplete(MockUrlRequestJobFactory.getMockUrlWithF ailure( 658 startAndWaitForComplete(MockUrlRequestJobFactory.getMockUrlWithF ailure(
657 FailurePhase.READ_ASYNC, arbitraryNetError)); 659 FailurePhase.READ_ASYNC, arbitraryNetError));
658 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); 660 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
659 assertEquals(0, callback.mResponseInfo.getReceivedBytesCount()); 661 assertEquals(0, callback.mResponseInfo.getReceivedBytesCount());
660 assertNotNull(callback.mError); 662 assertNotNull(callback.mError);
661 assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCo de()); 663 assertEquals(arbitraryNetError, callback.mError.getCronetInternalErrorCo de());
662 assertEquals(0, callback.mRedirectCount); 664 assertEquals(0, callback.mRedirectCount);
663 assertTrue(callback.mOnErrorCalled); 665 assertTrue(callback.mOnErrorCalled);
664 assertEquals(callback.mResponseStep, ResponseStep.ON_RESPONSE_STARTED); 666 assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
665 } 667 }
666 668
667 /** 669 /**
668 * Tests that request continues when client certificate is requested. 670 * Tests that request continues when client certificate is requested.
669 */ 671 */
670 @SmallTest 672 @SmallTest
671 @Feature({"Cronet"}) 673 @Feature({"Cronet"})
672 @OnlyRunNativeCronet 674 @OnlyRunNativeCronet
673 public void testMockClientCertificateRequested() throws Exception { 675 public void testMockClientCertificateRequested() throws Exception {
674 TestUrlRequestCallback callback = startAndWaitForComplete( 676 TestUrlRequestCallback callback = startAndWaitForComplete(
(...skipping 14 matching lines...) Expand all
689 @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory 691 @OnlyRunNativeCronet // Java impl doesn't support MockUrlRequestJobFactory
690 public void testMockSSLCertificateError() throws Exception { 692 public void testMockSSLCertificateError() throws Exception {
691 TestUrlRequestCallback callback = startAndWaitForComplete( 693 TestUrlRequestCallback callback = startAndWaitForComplete(
692 MockUrlRequestJobFactory.getMockUrlForSSLCertificateError()); 694 MockUrlRequestJobFactory.getMockUrlForSSLCertificateError());
693 assertNull(callback.mResponseInfo); 695 assertNull(callback.mResponseInfo);
694 assertNotNull(callback.mError); 696 assertNotNull(callback.mError);
695 assertTrue(callback.mOnErrorCalled); 697 assertTrue(callback.mOnErrorCalled);
696 assertEquals(-201, callback.mError.getCronetInternalErrorCode()); 698 assertEquals(-201, callback.mError.getCronetInternalErrorCode());
697 assertEquals("Exception in CronetUrlRequest: net::ERR_CERT_DATE_INVALID" , 699 assertEquals("Exception in CronetUrlRequest: net::ERR_CERT_DATE_INVALID" ,
698 callback.mError.getMessage()); 700 callback.mError.getMessage());
699 assertEquals(callback.mResponseStep, ResponseStep.NOTHING); 701 assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
700 } 702 }
701 703
702 /** 704 /**
703 * Checks that the buffer is updated correctly, when starting at an offset. 705 * Checks that the buffer is updated correctly, when starting at an offset.
704 */ 706 */
705 @SmallTest 707 @SmallTest
706 @Feature({"Cronet"}) 708 @Feature({"Cronet"})
707 public void testSimpleGetBufferUpdates() throws Exception { 709 public void testSimpleGetBufferUpdates() throws Exception {
708 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 710 TestUrlRequestCallback callback = new TestUrlRequestCallback();
709 callback.setAutoAdvance(false); 711 callback.setAutoAdvance(false);
(...skipping 854 matching lines...) Expand 10 before | Expand all | Expand 10 after
1564 if (Log.isLoggable("TESTING", Log.VERBOSE)) { 1566 if (Log.isLoggable("TESTING", Log.VERBOSE)) {
1565 Log.v("TESTING", "Testing " + failureType + " during " + failureStep ); 1567 Log.v("TESTING", "Testing " + failureType + " during " + failureStep );
1566 } 1568 }
1567 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1569 TestUrlRequestCallback callback = new TestUrlRequestCallback();
1568 callback.setFailure(failureType, failureStep); 1570 callback.setFailure(failureType, failureStep);
1569 UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.get RedirectURL(), 1571 UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.get RedirectURL(),
1570 callback, callback.getExecutor(), mTestFramework.mCronetEngine); 1572 callback, callback.getExecutor(), mTestFramework.mCronetEngine);
1571 UrlRequest urlRequest = builder.build(); 1573 UrlRequest urlRequest = builder.build();
1572 urlRequest.start(); 1574 urlRequest.start();
1573 callback.blockForDone(); 1575 callback.blockForDone();
1576 // Wait for all posted tasks to be executed to ensure there is no unhand led exception.
1577 callback.shutdownExecutorAndWait();
1574 assertEquals(1, callback.mRedirectCount); 1578 assertEquals(1, callback.mRedirectCount);
1575 assertEquals(callback.mResponseStep, failureStep); 1579 if (failureType == FailureType.CANCEL_SYNC || failureType == FailureType .CANCEL_ASYNC) {
1580 assertEquals(ResponseStep.ON_CANCELED, callback.mResponseStep);
1581 } else if (failureType == FailureType.THROW_SYNC) {
1582 assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
1583 }
1576 assertTrue(urlRequest.isDone()); 1584 assertTrue(urlRequest.isDone());
1577 assertEquals(expectResponseInfo, callback.mResponseInfo != null); 1585 assertEquals(expectResponseInfo, callback.mResponseInfo != null);
1578 assertEquals(expectError, callback.mError != null); 1586 assertEquals(expectError, callback.mError != null);
1579 assertEquals(expectError, callback.mOnErrorCalled); 1587 assertEquals(expectError, callback.mOnErrorCalled);
1580 assertEquals(failureType == FailureType.CANCEL_SYNC 1588 assertEquals(failureType == FailureType.CANCEL_SYNC
1581 || failureType == FailureType.CANCEL_ASYNC 1589 || failureType == FailureType.CANCEL_ASYNC
1582 || failureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE , 1590 || failureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE ,
1583 callback.mOnCanceledCalled); 1591 callback.mOnCanceledCalled);
1584 } 1592 }
1585 1593
(...skipping 23 matching lines...) Expand all
1609 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_READ_COMPLETED, 1617 throwOrCancel(FailureType.CANCEL_ASYNC, ResponseStep.ON_READ_COMPLETED,
1610 true, false); 1618 true, false);
1611 throwOrCancel(FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, ResponseStep.ON_RE AD_COMPLETED, 1619 throwOrCancel(FailureType.CANCEL_ASYNC_WITHOUT_PAUSE, ResponseStep.ON_RE AD_COMPLETED,
1612 true, false); 1620 true, false);
1613 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_READ_COMPLETED, 1621 throwOrCancel(FailureType.THROW_SYNC, ResponseStep.ON_READ_COMPLETED,
1614 true, true); 1622 true, true);
1615 } 1623 }
1616 1624
1617 @SmallTest 1625 @SmallTest
1618 @Feature({"Cronet"}) 1626 @Feature({"Cronet"})
1619 public void testThrowON_SUCCEEDED() { 1627 public void testThrowOrCancelInOnSucceeded() {
1620 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1628 FailureType[] testTypes = new FailureType[] {
1621 callback.setFailure(FailureType.THROW_SYNC, ResponseStep.ON_SUCCEEDED); 1629 FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CAN CEL_ASYNC};
1622 UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.get RedirectURL(), 1630 for (FailureType type : testTypes) {
1623 callback, callback.getExecutor(), mTestFramework.mCronetEngine); 1631 TestUrlRequestCallback callback = new TestUrlRequestCallback();
1624 UrlRequest urlRequest = builder.build(); 1632 callback.setFailure(type, ResponseStep.ON_SUCCEEDED);
1625 urlRequest.start(); 1633 UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer .getEchoMethodURL(),
1626 callback.blockForDone(); 1634 callback, callback.getExecutor(), mTestFramework.mCronetEngi ne);
1627 assertEquals(1, callback.mRedirectCount); 1635 UrlRequest urlRequest = builder.build();
1628 assertEquals(callback.mResponseStep, ResponseStep.ON_SUCCEEDED); 1636 urlRequest.start();
1629 assertTrue(urlRequest.isDone()); 1637 callback.blockForDone();
1630 assertNotNull(callback.mResponseInfo); 1638 // Wait for all posted tasks to be executed to ensure there is no un handled exception.
1631 assertNull(callback.mError); 1639 callback.shutdownExecutorAndWait();
1632 assertFalse(callback.mOnErrorCalled); 1640 assertNull(callback.mError);
1641 assertEquals(ResponseStep.ON_SUCCEEDED, callback.mResponseStep);
1642 assertTrue(urlRequest.isDone());
1643 assertNotNull(callback.mResponseInfo);
1644 assertFalse(callback.mOnErrorCalled);
1645 assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
1646 assertEquals("GET", callback.mResponseAsString);
1647 }
1633 } 1648 }
1634 1649
1635 @SmallTest 1650 @SmallTest
1651 @Feature({"Cronet"})
1652 public void testThrowOrCancelInOnFailed() {
1653 FailureType[] testTypes = new FailureType[] {
1654 FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CAN CEL_ASYNC};
1655 for (FailureType type : testTypes) {
1656 String url = NativeTestServer.getEchoBodyURL();
1657 // Shut down NativeTestServer so request will fail.
1658 NativeTestServer.shutdownNativeTestServer();
1659 TestUrlRequestCallback callback = new TestUrlRequestCallback();
1660 callback.setFailure(type, ResponseStep.ON_FAILED);
1661 UrlRequest.Builder builder = new UrlRequest.Builder(
1662 url, callback, callback.getExecutor(), mTestFramework.mCrone tEngine);
1663 UrlRequest urlRequest = builder.build();
1664 urlRequest.start();
1665 callback.blockForDone();
1666 // Wait for all posted tasks to be executed to ensure there is no un handled exception.
1667 callback.shutdownExecutorAndWait();
1668 assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
1669 assertTrue(callback.mOnErrorCalled);
1670 assertNotNull(callback.mError);
1671 assertTrue(urlRequest.isDone());
1672 // Start NativeTestServer again to run the test for a second time.
1673 assertTrue(NativeTestServer.startNativeTestServer(getContext()));
1674 }
1675 }
1676
1677 @SmallTest
1678 @Feature({"Cronet"})
1679 public void testThrowOrCancelInOnCanceled() {
1680 FailureType[] testTypes = new FailureType[] {
1681 FailureType.THROW_SYNC, FailureType.CANCEL_SYNC, FailureType.CAN CEL_ASYNC};
1682 for (FailureType type : testTypes) {
1683 TestUrlRequestCallback callback = new TestUrlRequestCallback() {
1684 @Override
1685 public void onResponseStarted(UrlRequest request, UrlResponseInf o info) {
1686 super.onResponseStarted(request, info);
1687 request.cancel();
1688 }
1689 };
1690 callback.setFailure(type, ResponseStep.ON_CANCELED);
1691 UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer .getEchoBodyURL(),
1692 callback, callback.getExecutor(), mTestFramework.mCronetEngi ne);
1693 UrlRequest urlRequest = builder.build();
1694 urlRequest.start();
1695 callback.blockForDone();
1696 // Wait for all posted tasks to be executed to ensure there is no un handled exception.
1697 callback.shutdownExecutorAndWait();
1698 assertEquals(ResponseStep.ON_CANCELED, callback.mResponseStep);
1699 assertTrue(urlRequest.isDone());
1700 assertNotNull(callback.mResponseInfo);
1701 assertNull(callback.mError);
1702 assertTrue(callback.mOnCanceledCalled);
1703 }
1704 }
1705
1706 @SmallTest
1636 @Feature({"Cronet"}) 1707 @Feature({"Cronet"})
1637 @OnlyRunNativeCronet // No destroyed callback for tests 1708 @OnlyRunNativeCronet // No destroyed callback for tests
1638 public void testExecutorShutdown() { 1709 public void testExecutorShutdown() {
1639 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1710 TestUrlRequestCallback callback = new TestUrlRequestCallback();
1640 1711
1641 callback.setAutoAdvance(false); 1712 callback.setAutoAdvance(false);
1642 UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.get EchoBodyURL(), 1713 UrlRequest.Builder builder = new UrlRequest.Builder(NativeTestServer.get EchoBodyURL(),
1643 callback, callback.getExecutor(), mTestFramework.mCronetEngine); 1714 callback, callback.getExecutor(), mTestFramework.mCronetEngine);
1644 CronetUrlRequest urlRequest = (CronetUrlRequest) builder.build(); 1715 CronetUrlRequest urlRequest = (CronetUrlRequest) builder.build();
1645 urlRequest.start(); 1716 urlRequest.start();
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
1830 TestUrlRequestCallback callback = startAndWaitForComplete( 1901 TestUrlRequestCallback callback = startAndWaitForComplete(
1831 MockUrlRequestJobFactory.getMockUrlWithFailure(FailurePhase.STAR T, netError)); 1902 MockUrlRequestJobFactory.getMockUrlWithFailure(FailurePhase.STAR T, netError));
1832 assertNull(callback.mResponseInfo); 1903 assertNull(callback.mResponseInfo);
1833 assertNotNull(callback.mError); 1904 assertNotNull(callback.mError);
1834 assertEquals(netError, callback.mError.getCronetInternalErrorCode()); 1905 assertEquals(netError, callback.mError.getCronetInternalErrorCode());
1835 assertEquals(errorCode, callback.mError.getErrorCode()); 1906 assertEquals(errorCode, callback.mError.getErrorCode());
1836 assertEquals( 1907 assertEquals(
1837 "Exception in CronetUrlRequest: net::ERR_" + name, callback.mErr or.getMessage()); 1908 "Exception in CronetUrlRequest: net::ERR_" + name, callback.mErr or.getMessage());
1838 assertEquals(0, callback.mRedirectCount); 1909 assertEquals(0, callback.mRedirectCount);
1839 assertTrue(callback.mOnErrorCalled); 1910 assertTrue(callback.mOnErrorCalled);
1840 assertEquals(callback.mResponseStep, ResponseStep.NOTHING); 1911 assertEquals(ResponseStep.ON_FAILED, callback.mResponseStep);
1841 } 1912 }
1842 1913
1843 // Returns the contents of byteBuffer, from its position() to its limit(), 1914 // Returns the contents of byteBuffer, from its position() to its limit(),
1844 // as a String. Does not modify byteBuffer's position(). 1915 // as a String. Does not modify byteBuffer's position().
1845 private String bufferContentsToString(ByteBuffer byteBuffer, int start, int end) { 1916 private String bufferContentsToString(ByteBuffer byteBuffer, int start, int end) {
1846 // Use a duplicate to avoid modifying byteBuffer. 1917 // Use a duplicate to avoid modifying byteBuffer.
1847 ByteBuffer duplicate = byteBuffer.duplicate(); 1918 ByteBuffer duplicate = byteBuffer.duplicate();
1848 duplicate.position(start); 1919 duplicate.position(start);
1849 duplicate.limit(end); 1920 duplicate.limit(end);
1850 byte[] contents = new byte[duplicate.remaining()]; 1921 byte[] contents = new byte[duplicate.remaining()];
1851 duplicate.get(contents); 1922 duplicate.get(contents);
1852 return new String(contents); 1923 return new String(contents);
1853 } 1924 }
1854 } 1925 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698