/*
 *   Copyright 2016 Author:NU11 bestoapache@gmail.com
 *
 *  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 shadowsocks;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import static org.junit.Assert.*;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import shadowsocks.util.GlobalConfig;
import shadowsocks.Shadowsocks;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Proxy;
import java.net.Socket;
import java.net.Proxy.Type;
import java.net.InetSocketAddress;
import java.net.URL;
import java.net.HttpURLConnection;
import java.util.Arrays;

public class SystemTest{

    public static Logger log = LogManager.getLogger(SystemTest.class.getName());

    @Before
    public void setUp(){
        log.info("Set up");
        GlobalConfig.get().setPassowrd("testkey");
        GlobalConfig.get().setMethod("aes-128-cfb");
        GlobalConfig.get().setServer("127.0.0.1");
        GlobalConfig.get().setPort(1024);
        GlobalConfig.get().setLocalPort(2048);
        GlobalConfig.get().setOTAEnabled(true);
        GlobalConfig.get().setTimeout(100);
    }
    @After
    public void tearDown(){
        log.info("Tear down");
    }
    @Test
    public void testStartStop() {

        Shadowsocks server = new Shadowsocks(true);
        Shadowsocks local = new Shadowsocks(false);
        //Can't shutdown before boot.
        assertFalse(server.shutdown());
        //Boot and shutdown
        assertTrue(server.boot());
        assertTrue(local.boot());
        assertTrue(server.shutdown());
        assertTrue(local.shutdown());
        //Boot again
        assertTrue(server.boot());
        //Two instances is not allowed.
        assertFalse(server.boot());
        assertTrue(server.shutdown());

    }

    private void testSimpleHttp(boolean ota) {

        GlobalConfig.get().setOTAEnabled(ota);

        Shadowsocks server = new Shadowsocks(true);
        Shadowsocks local = new Shadowsocks(false);

        assertTrue(server.boot());
        assertTrue(local.boot());
        Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 2048));
        HttpURLConnection conn = null;
        try{
            URL url = new URL("http://example.com");
            conn = (HttpURLConnection)url.openConnection(proxy);
            conn.setRequestMethod("GET");
            DataInputStream in1 = new DataInputStream(this.getClass().getClassLoader().getResourceAsStream("result-example-com"));
            byte [] expect = new byte[8192];
            int dataLen = in1.read(expect);
            DataInputStream in2 = new DataInputStream(conn.getInputStream());
            byte [] result = new byte[8192];
            in2.readFully(result, 0, dataLen);
            boolean compareResult = Arrays.equals(result, expect);
            if (!compareResult) {
                log.debug("====================");
                log.debug(new String(result));
                log.debug("====================");
            }
            assertTrue(compareResult);
        }catch(IOException e){
            log.error("Failed with exception.", e);
            fail();
        }finally{
            if (conn != null) {
                conn.disconnect();
            }
            assertTrue(server.shutdown());
            assertTrue(local.shutdown());
        }
    }
    @Test
    public void testHttp() {
        String [] methodList = {
            "aes-128-cfb",
            "aes-128-ofb",
            "aes-192-cfb",
            "aes-192-ofb",
            "aes-256-cfb",
            "aes-256-ofb",
            "chacha20"
        };
        for (String method: methodList) {
            GlobalConfig.get().setMethod(method);
            log.debug("Test method: " + method);
            testSimpleHttp(true);
        }
    }

    @Test
    public void testHttpWithoutOTA() {
        testSimpleHttp(false);
    }
}

@@ -2467,24 +2467,362 @@
 ));%0A%0A    %7D%0A%0A
+    @Test%0A    public void testStartFailed() %7B%0A        Shadowsocks local1 = new Shadowsocks(false);%0A        Shadowsocks local2 = new Shadowsocks(false);%0A        assertTrue(local1.boot());%0A        //Port had been bind%0A        assertFalse(local2.boot());%0A        assertTrue(local1.shutdown());%0A        assertFalse(local2.shutdown());%0A    %7D%0A%0A
     private 
@@ -4925,11 +4925,414 @@
 );%0A    %7D
+%0A%0A    @Test%0A    public void testInvalidLocalConnection() %7B%0A        Shadowsocks local = new Shadowsocks(false);%0A        assertTrue(local.boot());%0A        try(Socket s = new Socket(%22127.0.0.1%22, 2048)) %7B%0A            Thread.sleep(500);%0A        %7Dcatch (Exception e)%7B%0A            log.error(%22Catch exception.%22, e);%0A            fail();%0A        %7Dfinally%7B%0A            assertTrue(local.shutdown());%0A        %7D%0A    %7D
 %0A%7D%0A

Add some test.