/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * 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.alibaba.nacos.common.utils;

import org.apache.commons.io.Charsets;
import org.junit.jupiter.api.Test;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * Unit test of IoUtils.
 *
 * @author karsonto
 */
class IoUtilsTest {
    
    @Test
    void testTryDecompressForNotGzip() throws Exception {
        byte[] testCase = "123".getBytes(Charsets.toCharset("UTF-8"));
        assertEquals(testCase, IoUtils.tryDecompress(testCase));
    }
    
    @Test
    void testTryDecompressForGzip() throws Exception {
        byte[] testCase = IoUtils.tryCompress("123", "UTF-8");
        assertEquals("123", new String(IoUtils.tryDecompress(testCase), StandardCharsets.UTF_8));
    }
    
    @Test
    void testTryCompressWithEmptyString() {
        assertEquals(0, IoUtils.tryCompress("", "UTF-8").length);
        assertEquals(0, IoUtils.tryCompress(null, "UTF-8").length);
    }
    
    @Test
    void testWriteStringToFile() throws IOException {
        File file = null;
        try {
            file = File.createTempFile("test_writeStringToFile", ".txt");
            IoUtils.writeStringToFile(file, "123", "UTF-8");
            List<String> actual = IoUtils.readLines(new FileReader(file));
            assertEquals(1, actual.size());
            assertEquals("123", actual.get(0));
        } finally {
            if (null != file) {
                file.deleteOnExit();
            }
        }
    }
    
    @Test
    void testToStringWithNull() throws IOException {
        assertEquals("", IoUtils.toString(null, "UTF-8"));
    }
    
    @Test
    void testToStringWithReader() throws IOException {
        String testCase = "123";
        assertEquals(testCase,
                IoUtils.toString(new ByteArrayInputStream(testCase.getBytes(Charsets.toCharset("UTF-8"))), "UTF-8"));
    }
    
    @Test
    void testDeleteForNullFile() throws IOException {
        IoUtils.delete(null);
    }
    
    @Test
    void testDeleteSuccess() throws IOException {
        File file = null;
        try {
            file = File.createTempFile("test_deleteForFile", ".txt");
            assertTrue(file.exists());
            IoUtils.delete(file);
            assertFalse(file.exists());
        } finally {
            if (null != file) {
                file.deleteOnExit();
            }
        }
    }
    
    @Test
    void testDeleteFileFailure() throws IOException {
        assertThrows(IOException.class, () -> {
            File file = mock(File.class);
            when(file.exists()).thenReturn(true);
            when(file.delete()).thenReturn(false);
            IoUtils.delete(file);
        });
    }
    
    @Test
    void testDeleteForDirectory() throws IOException {
        File file = null;
        try {
            String tmpDir = AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty("java.io.tmpdir"));
            File tmpDirFile = new File(tmpDir, "IoUtilsTest");
            tmpDirFile.mkdirs();
            file = File.createTempFile("test_deleteForDirectory", ".txt", tmpDirFile);
            assertTrue(file.exists());
            IoUtils.delete(file.getParentFile());
            assertTrue(tmpDirFile.exists());
            assertFalse(file.exists());
        } finally {
            if (null != file) {
                file.getParentFile().deleteOnExit();
                file.deleteOnExit();
            }
        }
    }
    
    @Test
    void testCleanDirectoryForNonExistingDirectory() throws IOException {
        assertThrows(IllegalArgumentException.class, () -> {
            File nonexistentDir = new File("non_exist");
            IoUtils.cleanDirectory(nonexistentDir);
        });
    }
    
    @Test
    void testCleanDirectoryForFile() throws IOException {
        assertThrows(IllegalArgumentException.class, () -> {
            File mockFile = mock(File.class);
            when(mockFile.exists()).thenReturn(true);
            IoUtils.cleanDirectory(mockFile);
        });
    }
    
    @Test
    void testCleanDirectoryWithEmptyDirectory() throws IOException {
        assertThrows(IOException.class, () -> {
            File mockFile = mock(File.class);
            when(mockFile.exists()).thenReturn(true);
            when(mockFile.isDirectory()).thenReturn(true);
            IoUtils.cleanDirectory(mockFile);
        });
    }
    
    @Test
    void testCleanDirectory() throws IOException {
        assertThrows(IOException.class, () -> {
            File mockFile = mock(File.class);
            when(mockFile.exists()).thenReturn(true);
            when(mockFile.isDirectory()).thenReturn(true);
            File mockSubFile = mock(File.class);
            when(mockSubFile.exists()).thenReturn(true);
            when(mockFile.listFiles()).thenReturn(new File[] {mockSubFile});
            IoUtils.cleanDirectory(mockFile);
        });
    }
    
    @Test
    void testIsGzipStreamWithNull() {
        assertFalse(IoUtils.isGzipStream(null));
    }
    
    @Test
    void testIsGzipStreamWithEmpty() {
        assertFalse(IoUtils.isGzipStream(new byte[0]));
    }
    
    @Test
    void testCloseQuietly() throws IOException {
        BufferedReader br = new BufferedReader(
                new InputStreamReader(new ByteArrayInputStream("111".getBytes(Charsets.toCharset("UTF-8")))));
        assertEquals("111", br.readLine());
        IoUtils.closeQuietly(br);
        try {
            br.readLine();
        } catch (IOException e) {
            assertNotNull(e);
            return;
        }
        fail();
    }
    
    @Test
    void testCloseQuietly2() throws IOException {
        BufferedReader br = new BufferedReader(
                new InputStreamReader(new ByteArrayInputStream("123".getBytes(Charsets.toCharset("UTF-8")))));
        assertEquals("123", br.readLine());
        BufferedReader br2 = new BufferedReader(
                new InputStreamReader(new ByteArrayInputStream("456".getBytes(Charsets.toCharset("UTF-8")))));
        assertEquals("456", br2.readLine());
        IoUtils.closeQuietly(br, br2);
        try {
            br.readLine();
        } catch (IOException e) {
            assertNotNull(e);
        }
        try {
            br2.readLine();
        } catch (IOException e) {
            assertNotNull(e);
            return;
        }
        fail();
    }
    
    @Test
    void testCloseQuietlyForHttpConnection() throws IOException {
        HttpURLConnection conn = mock(HttpURLConnection.class);
        InputStream inputStream = mock(InputStream.class);
        when(conn.getInputStream()).thenReturn(inputStream);
        IoUtils.closeQuietly(conn);
        verify(inputStream).close();
    }
    
}
