/* * Copyright (C) 2008 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 android.core; import junit.framework.Assert; import junit.framework.TestCase; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.nio.channels.SocketChannel; import java.util.concurrent.Semaphore; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.Suppress; /** * Regression tests for various socket related problems. And a few general * socket tests. */ public class SocketTest extends TestCase { private static final String NON_EXISTING_ADDRESS = "123.123.123.123"; private static final String KNOW_GOOD_ADDRESS = "209.85.129.147"; private static final String PACKAGE_DROPPING_ADDRESS = "191.167.0.1"; // Test for basic bind/connect/accept behavior. @SmallTest public void testSocketSimple() throws Exception { ServerSocket ss; Socket s, s1; int port; IOException lastEx = null; ss = new ServerSocket(); for (port = 9900; port < 9999; port++) { try { ss.bind(new InetSocketAddress("127.0.0.1", port)); lastEx = null; break; } catch (IOException ex) { lastEx = ex; } } if (lastEx != null) { throw lastEx; } s = new Socket("127.0.0.1", port); s1 = ss.accept(); s.getOutputStream().write(0xa5); assertEquals(0xa5, s1.getInputStream().read()); s1.getOutputStream().write(0x5a); assertEquals(0x5a, s.getInputStream().read()); } // Regression test for #820068: Wildcard address @SmallTest public void testWildcardAddress() throws Exception { Socket s2 = new Socket(); s2.bind(new InetSocketAddress((InetAddress) null, 12345)); byte[] addr = s2.getLocalAddress().getAddress(); for (int i = 0; i < 4; i++) { assertEquals("Not the wildcard address", 0, addr[i]); } } // Regression test for #865753: server sockets not closing properly @SmallTest public void testServerSocketClose() throws Exception { ServerSocket s3 = new ServerSocket(23456); s3.close(); ServerSocket s4 = new ServerSocket(23456); s4.close(); } // Regression test for #876985: SO_REUSEADDR not working properly private Exception serverError = null; @LargeTest public void testSetReuseAddress() throws IOException { InetSocketAddress addr = new InetSocketAddress(8383); final ServerSocket serverSock = new ServerSocket(); serverSock.setReuseAddress(true); serverSock.bind(addr); final Semaphore semThreadEnd = new Semaphore(0); new Thread() { @Override public void run() { try { Socket sock = serverSock.accept(); sock.getInputStream().read(); sock.close(); } catch (IOException e) { serverError = e; } semThreadEnd.release(); } }.start(); // Give the server a bit of time for startup try { Thread.sleep(2000); } catch (InterruptedException ex) { // Ignored. } Socket client = new Socket("localhost", 8383); client.getOutputStream().write(1); // Just leave this connection open from the client side. It will be // closed from the server side so the server stays in the TIME_WAIT // state for a while. setReuseAddress() should be able to handle this. try { semThreadEnd.acquire(); } catch (InterruptedException e) { // ignore } serverSock.close(); ServerSocket serverSock2 = new ServerSocket(); serverSock2.setReuseAddress(true); serverSock2.bind(addr); serverSock2.close(); if (serverError != null) { throw new RuntimeException("Server must complete without error", serverError); } } // Regression for 916701, a wrong exception was thrown after timeout of // a ServerSocket. @LargeTest public void testTimeoutException() throws IOException { ServerSocket s = new ServerSocket(9800); s.setSoTimeout(2000); try { s.accept(); } catch (SocketTimeoutException e) { // this is ok. } } // Regression for issue 1001980, openening a SocketChannel threw an Exception @SmallTest public void testNativeSocketChannelOpen() throws IOException { SocketChannel.open(); } // Regression test for issue 1018016, connecting ignored a set timeout. // // Disabled because test behaves differently depending on networking // environment. It works fine in the emulator and one the device with // WLAN, but when 3G comes into play, the possible existence of a // proxy makes it fail. // // @LargeTest // public void testSocketSetSOTimeout() throws IOException { // Socket sock = new Socket(); // int timeout = 5000; // long start = System.currentTimeMillis(); // try { // sock.connect(new InetSocketAddress(NON_EXISTING_ADDRESS, 80), timeout); // } catch (SocketTimeoutException e) { // // expected // long delay = System.currentTimeMillis() - start; // if (Math.abs(delay - timeout) > 1000) { // fail("timeout was not accurate. expected: " + timeout // + " actual: " + delay + " miliseconds."); // } // } finally { // try { // sock.close(); // } catch (IOException ioe) { // // ignore // } // } // } /** * Regression test for 1062928: Dotted IP addresses (e.g., 192.168.100.1) * appear to be broken in the M5 SDK. * * Tests that a connection given a ip-addressv4 such as 192.168.100.100 does * not fail - sdk m5 seems only to accept dns names instead of ip numbers. * ip 209.85.129.147 (one address of www.google.com) on port 80 (http) is * used to test the connection. */ // Commenting out this test since it is flaky, even at the best of times. See // #1191317 for Info. @Suppress public void disable_testConnectWithIP4IPAddr() { // call a Google Web server InetSocketAddress scktAddrss = new InetSocketAddress(KNOW_GOOD_ADDRESS, 80); Socket clntSckt = new Socket(); try { clntSckt.connect(scktAddrss, 5000); } catch (Throwable e) { fail("connection problem:" + e.getClass().getName() + ": " + e.getMessage()); } finally { try { clntSckt.close(); } catch (Exception e) { // ignore } } } // Regression test for #1058962: Socket.close() does not cause // socket.connect() to return immediately. private Socket client; private Exception error; private boolean connected; // This test isn't working now, but really should work. // TODO Enable this test again. @Suppress public void disable_testSocketConnectClose() { try { client = new Socket(); new Thread() { @Override public void run() { try { client.connect(new InetSocketAddress(PACKAGE_DROPPING_ADDRESS, 1357)); } catch (Exception ex) { error = ex; } connected = true; } }.start(); Thread.sleep(1000); Assert.assertNull("Connect must not fail immediately. Maybe try different address.", error); Assert.assertFalse("Connect must not succeed. Maybe try different address.", connected); client.close(); Thread.sleep(1000); if (error == null) { fail("Socket connect still ongoing"); } else if (!(error instanceof SocketException)) { fail("Socket connect interrupted with wrong error: " + error.toString()); } } catch (Exception ex) { throw new RuntimeException(ex); } } }