/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.text;

import java.util.Arrays;
import java.util.regex.Pattern;
import static net.ranides.assira.junit.NewAssert.*;
import org.junit.Test;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
@SuppressWarnings({
    "PMD.AvoidDuplicateLiterals",
    "PMD.TooManyPublicMethods",
    "PMD.TooManyDifferentMethods"
})
public class StringUtilsTest {
	
	public StringUtilsTest() {
	}

	@Test
	public void testQuote() {
		assertEquals("\"\"", StringUtils.quote(""));
		assertEquals("\"abc\"", StringUtils.quote("abc"));
		assertEquals("\"\\\"abc\\\"\"", StringUtils.quote("\"abc\""));
		assertEquals("\"a\\\"bc\"", StringUtils.quote("a\"bc"));
        
        assertEquals( "\"a\\\"bc c:\\\\v\"", StringUtils.quote("a\"bc c:\\v") );
        assertEquals( "\"a\\\"bc c:\\\\vdata\b \"", StringUtils.quote("a\"bc c:\\vdata\b ") );
        assertEquals( "\"a\\\"bc c:\\\\vdata\\\\\b \"", StringUtils.quote("a\"bc c:\\vdata\\\b ") );
	}
	
	@Test
	public void testUnquote() {
		assertEquals("", StringUtils.unquote(""));
        assertEquals("a", StringUtils.unquote("a") );
		assertEquals("abc", StringUtils.unquote("abc"));
        
        assertEquals("", StringUtils.unquote("\"\""));
        assertEquals("a", StringUtils.unquote("\"a\"") );
		assertEquals("abc", StringUtils.unquote("\"abc\""));
        
		assertEquals("\"abc\"", StringUtils.unquote("\"\\\"abc\\\"\""));
		assertEquals("a\"bc", StringUtils.unquote("\"a\\\"bc\""));
		assertEquals("a\\\"bc", StringUtils.unquote("a\\\"bc"));
        
        
        assertEquals("abc \\hello",     StringUtils.unquote("\"abc \\hello\"") );
        assertEquals("abc \\hello",     StringUtils.unquote("\"abc \\\\hello\"") );
        assertEquals("abc \"hello",     StringUtils.unquote("\"abc \\\\\"hello\"") );
        assertEquals("abc \\ \"hello",  StringUtils.unquote("\"abc \\\\ \"hello\"") );
        assertEquals("abc \\ \"hello",  StringUtils.unquote("\"abc \\\\ \\\"hello\"") );
        assertEquals("abc \\\"hello",   StringUtils.unquote("\"abc \\\\\\\"hello\"") );
        
        // invalid, ale niech będzie test takiej sytuacji. 
        // nie-escape'owane " po prostu zostawiamy
        assertEquals("abc \\\"\"hello",   StringUtils.unquote("\"abc \\\\\\\"\"hello\"") );
        assertEquals("abc \\\"\"he\"llo",   StringUtils.unquote("\"abc \\\\\\\"\"he\"llo\"") );
        
        assertThrows(IllegalArgumentException.class, ()->{
            StringUtils.unquote("\"");
        });
        assertThrows(IllegalArgumentException.class, ()->{
            StringUtils.unquote("abc\"");
        });
        assertThrows(IllegalArgumentException.class, ()->{
            StringUtils.unquote("\"abc");
        });
	}
	
	@Test
	public void testSplit() {
		assertEquals(Arrays.asList(), StringUtils.split("").list());
		assertEquals(Arrays.asList("a"), StringUtils.split("a").list());
		
		assertEquals(Arrays.asList("a", "bc", "d"), StringUtils.split("a bc d").list());
		assertEquals(Arrays.asList("a", "bc", "d"), StringUtils.split("a   bc d").list());
		assertEquals(Arrays.asList("a", "bc", "d"), StringUtils.split("a   bc   d  ").list());
		
		assertEquals(Arrays.asList("a", "bc q", "d"), StringUtils.split("a   \"bc q\"   d  ").list());
		assertEquals(Arrays.asList("a", "bc \" q", "d"), StringUtils.split("a   \"bc \\\" q\"   d  ").list());
	}
	
	@Test
	public void testCompile() {
        // kody \r \t \f \n \b
        assertEquals("\r \t \f \n \b", StringUtils.compile("\\r \\t \\f \\n \\b"));
        
        // kod \\
        assertEquals("c:\\win32\\new\n", StringUtils.compile("c:\\\\win32\\\\new\\n"));
        
        // kody dodatkowe \e \^
        assertEquals("escape \033 \033[ seqence", StringUtils.compile("escape \\e \\^ seqence"));
        
        // poprawne sekwencje OCT
        assertEquals("N\077A\042C", StringUtils.compile("N\\077A\\042C"));
        
        // sprawdzenie niepoprawnych sekwencji OCT
        assertEquals("N\23T\779", StringUtils.compile("N\\23T\\779"));
        
        // cudzysłów
        assertEquals("hello \" world", StringUtils.compile("hello \\\" world"));
        
        assertThrows(IllegalArgumentException.class, ()->{
            StringUtils.compile("hello \\\" world\\");
        });
        assertThrows(IllegalArgumentException.class, ()->{
            StringUtils.compile("hello \\ world");
        });
        assertThrows(IllegalArgumentException.class, ()->{
            StringUtils.compile("hello \\z world");
        });
	}
	
	@Test
	public void testRemove() {
		assertEquals("Hello world", StringUtils.remove("Heltumlo wortumld", "tum"));
		assertEquals("Hello world", StringUtils.remove("Heltumlo wortumldtum", "tum"));
		assertEquals("Hello world", StringUtils.remove("tumHeltumlo wortumldtum", "tum"));
		assertEquals("Hello world", StringUtils.remove("Helretlo wretorld", "ret"));
		assertEquals("Hello world", StringUtils.remove("Helretlo wretorldret", "ret"));
		assertEquals("Hello world", StringUtils.remove("retHelretlo wretorldret", "ret"));
		
		assertEquals("Hello world", StringUtils.remove("Helqlo wqorqld", "q"));
		assertEquals("Hello world", StringUtils.remove("qHelqlo wqorqld", "q"));
		assertEquals("Hello world", StringUtils.remove("Hello world", ""));
		assertEquals("Hello world", StringUtils.remove("Hello world", "tem"));
	}
	
	@Test
	public void testRemove_char() {
		assertEquals("Hello world", StringUtils.remove("Helqlo wqorqld", 'q'));
		assertEquals("Hello world", StringUtils.remove("Helqlo wqorqldq", 'q'));
		assertEquals("Hello world", StringUtils.remove("qHelqlo wqorqld", 'q'));
	}
	
	@Test
	public void testRemovePrefix() {
		assertEquals("Hello world", StringUtils.removePrefix("QeHello world", "Qe"));
		assertEquals("Hello world", StringUtils.removePrefix("HeHello world", "He"));
		assertEquals("Hello world", StringUtils.removePrefix("Hello world", "Ha"));
		
		assertEquals("", StringUtils.removePrefix("Ha", "Ha"));
		assertEquals("", StringUtils.removePrefix("", "Ha"));
		assertEquals("Ha", StringUtils.removePrefix("Ha", "Hat"));
	}
	
	@Test
	public void testRemoveSuffix() {
		assertEquals("Hello world", StringUtils.removeSuffix("Hello worldQe", "Qe"));
		assertEquals("Hello world", StringUtils.removeSuffix("Hello worldld", "ld"));
		assertEquals("Hello world", StringUtils.removeSuffix("Hello world", "ad"));
		
		assertEquals("", StringUtils.removeSuffix("Ha", "Ha"));
		assertEquals("", StringUtils.removeSuffix("", "Ha"));
		assertEquals("Ha", StringUtils.removeSuffix("Ha", "Hat"));
	}
	
	@Test
	public void testCondense() {
		assertEquals(" ", StringUtils.condense("    ", ' '));
		assertEquals(" ", StringUtils.condense(" ", ' '));
		assertEquals("", StringUtils.condense("", ' '));
		
		assertEquals("a b c", StringUtils.condense("a b c", ' '));
		assertEquals(" a b c", StringUtils.condense("   a b c", ' '));
		assertEquals(" a b c ", StringUtils.condense("   a b   c   ", ' '));
	}
	
	@Test
	public void testLtrim() {
		assertEquals("", StringUtils.ltrim("    "));
		assertEquals("abc  ", StringUtils.ltrim("    abc  "));
		assertEquals("abc  ", StringUtils.ltrim("abc  "));
	}
	
	@Test
	public void testRtrim() {
		assertEquals("", StringUtils.rtrim("    "));
		assertEquals("  abc", StringUtils.rtrim("  abc    "));
		assertEquals("  abc", StringUtils.rtrim("  abc"));
	}
	
	@Test
	public void testTrim() {
		assertEquals("", StringUtils.trim("    "));
		assertEquals("abc", StringUtils.trim("    abc  "));
		assertEquals("abc", StringUtils.trim("abc  "));
		assertEquals("abc", StringUtils.trim("  abc"));
	}
	
	@Test
	public void testReplaceFunction() {
		assertEquals("A=7 B=14 C=21", StringUtils.replace("A=#1 B=#2 C=#3", Pattern.compile("#([0-9])"), hit -> 
			String.valueOf(7*Integer.parseInt(hit.group(1))) 
		));
	}
	
	@Test
	public void testReplaceChar() {
        assertEquals("", StringUtils.replace("", '#', '$'));
        assertEquals("", StringUtils.replace("", '#', "$"));
        
		assertEquals("A=$1 B=$2 C=$3", StringUtils.replace("A=#1 B=#2 C=#3", '#', '$'));
		
		assertEquals("A=1 B=2 C=3", StringUtils.replace("A=#1 B=#2 C=#3", '#', ""));
        
        assertEquals("A=#1 B=#2 C=#3", StringUtils.replace("A=#1 B=#2 C=#3", '?', ""));
		
		assertEquals("A=d1 B=d2 C=d3", StringUtils.replace("A=#1 B=#2 C=#3", '#', "d"));
		
		assertEquals("A=num1 B=num2 C=num3", StringUtils.replace("A=#1 B=#2 C=#3", '#', "num"));
	}
	
	@Test
	public void testJoin() {
		assertEquals("", StringUtils.join(Arrays.asList(), ","));
		assertEquals("1", StringUtils.join(Arrays.asList("1"), ","));
		assertEquals("1,2", StringUtils.join(Arrays.asList("1","2"), ","));
		assertEquals("1,2,3", StringUtils.join(Arrays.asList("1","2","3"), ","));
		assertEquals("1,2,3,4", StringUtils.join(Arrays.asList("1","2","3","4"), ","));
		
		assertEquals("1234", StringUtils.join(Arrays.asList("1","2","3","4"), ""));
		assertEquals("1, 2, 3, 4", StringUtils.join(Arrays.asList("1","2","3","4"), ", "));
	}
	
	@Test
	public void testJoinFunction() {
		assertEquals("", StringUtils.join(Arrays.asList(), ",", Integer::toHexString));
		assertEquals("a", StringUtils.join(Arrays.asList(10), ",", Integer::toHexString));
		assertEquals("a,b", StringUtils.join(Arrays.asList(10,11), ",", Integer::toHexString));
		assertEquals("a,b,c", StringUtils.join(Arrays.asList(10,11,12), ",", Integer::toHexString));
		assertEquals("a,b,c,d", StringUtils.join(Arrays.asList(10,11,12,13), ",", Integer::toHexString));
		
		assertEquals("abcd", StringUtils.join(Arrays.asList(10,11,12,13), "", Integer::toHexString));
		assertEquals("a, b, c, d", StringUtils.join(Arrays.asList(10,11,12,13), ", ", Integer::toHexString));
	}
	
	@Test
	public void testJoinArray() {
		assertEquals("", StringUtils.join(new String[]{}, ","));
		assertEquals("1", StringUtils.join(new String[]{"1"}, ","));
		assertEquals("1,2", StringUtils.join(new String[]{"1","2"}, ","));
		assertEquals("1,2,3", StringUtils.join(new String[]{"1","2","3"}, ","));
		assertEquals("1,2,3,4", StringUtils.join(new String[]{"1","2","3","4"}, ","));
		
		assertEquals("1234", StringUtils.join(new String[]{"1","2","3","4"}, ""));
		assertEquals("1, 2, 3, 4", StringUtils.join(new String[]{"1","2","3","4"}, ", "));
	}
	
	@Test
	public void testJoinArrayFunction() {
		assertEquals("", StringUtils.join(new Integer[]{}, ",", Integer::toHexString));
		assertEquals("a", StringUtils.join(new Integer[]{10}, ",", Integer::toHexString));
		assertEquals("a,b", StringUtils.join(new Integer[]{10,11}, ",", Integer::toHexString));
		assertEquals("a,b,c", StringUtils.join(new Integer[]{10,11,12}, ",", Integer::toHexString));
		assertEquals("a,b,c,d", StringUtils.join(new Integer[]{10,11,12,13}, ",", Integer::toHexString));
		
		assertEquals("abcd", StringUtils.join(new Integer[]{10,11,12,13}, "", Integer::toHexString));
		assertEquals("a, b, c, d", StringUtils.join(new Integer[]{10,11,12,13}, ", ", Integer::toHexString));
	}
	
	@Test
	public void testCapitalize() {
		assertEquals("", StringUtils.capitalize(""));
		assertEquals("A", StringUtils.capitalize("a"));
		assertEquals("Ąę", StringUtils.capitalize("ąę"));
		
		assertEquals("Hello", StringUtils.capitalize("hello"));
		assertEquals("HeLLo", StringUtils.capitalize("heLLo"));
		assertEquals("Hello", StringUtils.capitalize("Hello"));
	}
	
	@Test
	public void testUncapitalize() {
		assertEquals("", StringUtils.uncapitalize(""));
		assertEquals("a", StringUtils.uncapitalize("A"));
		assertEquals("ąę", StringUtils.uncapitalize("Ąę"));
		
		assertEquals("hello", StringUtils.uncapitalize("Hello"));
		assertEquals("hELLo", StringUtils.uncapitalize("HELLo"));
		assertEquals("hello", StringUtils.uncapitalize("hello"));
	}
    
	@Test
	public void testCamelize() {
		assertEquals("", StringUtils.camelize(""));
        assertEquals("", StringUtils.camelize("_"));
        assertEquals("", StringUtils.camelize("___"));
        
		assertEquals("do", StringUtils.camelize("do"));
		assertEquals("doOpen", StringUtils.camelize("do_open"));
		assertEquals("doOpenFile", StringUtils.camelize("do_open_file"));
		
		assertEquals("doOpenFile", StringUtils.camelize("do_open_file_"));
		assertEquals("doOpenFile", StringUtils.camelize("_do_open_file_"));
        assertEquals("doOpenFile", StringUtils.camelize("_do__open___file_"));
	}
    
    @Test
	public void testUncamelize() {
        assertEquals("", StringUtils.uncamelize(""));
        assertEquals("load", StringUtils.uncamelize("load"));
        assertEquals("load", StringUtils.uncamelize("Load"));
        
        assertEquals("load_file", StringUtils.uncamelize("loadFile"));
        assertEquals("load_file_name", StringUtils.uncamelize("loadFileName"));
        assertEquals("load_file_name", StringUtils.uncamelize("LoadFileName"));

        assertEquals("open_usb", StringUtils.uncamelize("openUSB"));
        assertEquals("open_usb_port", StringUtils.uncamelize("openUSBPort"));
        assertEquals("open_usb_port_name", StringUtils.uncamelize("openUSBPortName"));
    }
    
    @Test
    public void testClip() {
        assertEquals("", StringUtils.clip("", 0));
        assertEquals("", StringUtils.clip("", 3));
        assertEquals("", StringUtils.clip("", -2));
        
        assertEquals("", StringUtils.clip("hello", 0));
        assertEquals("", StringUtils.clip("hello", -2));
        
        assertEquals("he", StringUtils.clip("he", 3));
        assertEquals("hel", StringUtils.clip("hello", 3));
    }
    
    @Test
    public void testLPadd() {
        assertEquals("", StringUtils.lpadd("", '-', 0));
        assertEquals("---", StringUtils.lpadd("", '-', 3));
        assertEquals("", StringUtils.lpadd("", '-', -2));
        
        assertEquals("hello", StringUtils.lpadd("hello", '-', 0));
        assertEquals("hello", StringUtils.lpadd("hello", '-', 3));
        assertEquals("hello", StringUtils.lpadd("hello", '-', 5));
        
        assertEquals("hello-", StringUtils.lpadd("hello", '-', 6));
        assertEquals("hello--", StringUtils.lpadd("hello", '-', 7));
    }
    
    @Test
    public void testRPadd() {
        assertEquals("", StringUtils.rpadd("", '-', 0));
        assertEquals("---", StringUtils.rpadd("", '-', 3));
        assertEquals("", StringUtils.rpadd("", '-', -2));
        
        assertEquals("hello", StringUtils.rpadd("hello", '-', 0));
        assertEquals("hello", StringUtils.rpadd("hello", '-', 3));
        assertEquals("hello", StringUtils.rpadd("hello", '-', 5));
        
        assertEquals("-hello", StringUtils.rpadd("hello", '-', 6));
        assertEquals("--hello", StringUtils.rpadd("hello", '-', 7));
    }
    
    @Test
    public void testCommonPrefix() {
        assertEquals("",   StringUtils.commonPrefix(Arrays.asList()) );
        assertEquals("z",  StringUtils.commonPrefix(Arrays.asList("z")) );
        assertEquals("z",  StringUtils.commonPrefix(Arrays.asList("z","z")) );
        assertEquals("z",  StringUtils.commonPrefix(Arrays.asList("z","za")) );
        assertEquals("z",  StringUtils.commonPrefix(Arrays.asList("ze","za")) );
        assertEquals("",   StringUtils.commonPrefix(Arrays.asList("re","za")) );
        
        assertEquals("r",   StringUtils.commonPrefix(Arrays.asList("re","rea", "rsu", "ret")) );
        assertEquals("re",  StringUtils.commonPrefix(Arrays.asList("rea","reo", "rei", "ree")) );
        assertEquals("",    StringUtils.commonPrefix(Arrays.asList("rea","reo", "rei", "dea")) );
        assertEquals("",    StringUtils.commonPrefix(Arrays.asList("rea","reo", "rei", "")) );
    }
    
    @Test
    public void testCommonSuffix() {
        assertEquals("",   StringUtils.commonSuffix(Arrays.asList()) );
        assertEquals("z",  StringUtils.commonSuffix(Arrays.asList("z")) );
        assertEquals("z",  StringUtils.commonSuffix(Arrays.asList("z","z")) );
        assertEquals("z",  StringUtils.commonSuffix(Arrays.asList("z","az")) );
        assertEquals("z",  StringUtils.commonSuffix(Arrays.asList("az","z")) );
        
        assertEquals("rit",  StringUtils.commonSuffix(Arrays.asList("rit","rit")) );
        assertEquals("rit",  StringUtils.commonSuffix(Arrays.asList("arit","rit")) );
        assertEquals("rit",  StringUtils.commonSuffix(Arrays.asList("rit","arit")) );

        assertEquals("ri", StringUtils.commonSuffix("eri","ari") );
        assertEquals("ri", StringUtils.commonSuffix("teri","mari") );
        
        assertEquals("i", StringUtils.commonSuffix(Arrays.asList("ri","zi")) );
        assertEquals("ri", StringUtils.commonSuffix(Arrays.asList("eri","ari")) );
        assertEquals("ri", StringUtils.commonSuffix(Arrays.asList("teri","mari")) );
        
        assertEquals("r",   StringUtils.commonSuffix(Arrays.asList("er", "aer", "usr", "ter")) );
        assertEquals("er",  StringUtils.commonSuffix(Arrays.asList("aer","oer", "ier", "eer")) );
        assertEquals("",    StringUtils.commonSuffix(Arrays.asList("aer","oer", "ier", "aed")) );
        assertEquals("",    StringUtils.commonSuffix(Arrays.asList("aer","oer", "ier", "")) );
    }
    
    @Test
    public void testReverse() {
        assertEquals("", StringUtils.reverse(""));
        assertEquals("a", StringUtils.reverse("a"));
        assertEquals("ba", StringUtils.reverse("ab"));
        assertEquals("cba", StringUtils.reverse("abc"));
        assertEquals("edcba", StringUtils.reverse("abcde"));
    }
    
    @Test
    public void testBefore() {
        assertEquals("", StringUtils.before("", "r"));
        assertEquals("", StringUtils.before("hello", ""));
        assertEquals("", StringUtils.before("hello", "r"));
        assertEquals("", StringUtils.before("hello", "he"));
        
        assertEquals("he", StringUtils.before("hello", "ll"));
        assertEquals("hell", StringUtils.before("hello", "o"));
        assertEquals("he", StringUtils.before("hello", "l"));
    }
    
    @Test
    public void testAfter() {
        assertEquals("", StringUtils.after("", "r"));
        assertEquals("", StringUtils.after("hello", "r"));
        assertEquals("", StringUtils.after("hello", "lo"));
        
        assertEquals("hello", StringUtils.after("hello", ""));
        
        assertEquals("ord", StringUtils.after("hellord", "ll"));
        assertEquals("llord", StringUtils.after("hellord", "e"));
        assertEquals("lord", StringUtils.after("hellord", "l"));
    }
    
    @Test
    public void testBetweenB() {
        assertEquals("", StringUtils.between("", null, "r"));
        assertEquals("", StringUtils.between("hello", null, ""));
        assertEquals("", StringUtils.between("hello", null, "r"));
        assertEquals("", StringUtils.between("hello", null, "he"));
        
        assertEquals("he", StringUtils.between("hello", null, "ll"));
        assertEquals("hell", StringUtils.between("hello", null, "o"));
        assertEquals("he", StringUtils.between("hello", null, "l"));
    }
    
    @Test
    public void testBetweenA() {
        assertEquals("", StringUtils.between("", "r", null));
        assertEquals("", StringUtils.between("hello", "r", null));
        assertEquals("", StringUtils.between("hello", "lo", null));
        
        assertEquals("hello", StringUtils.between("hello", "", null));
        
        assertEquals("ord", StringUtils.between("hellord", "ll", null));
        assertEquals("llord", StringUtils.between("hellord", "e", null));
        assertEquals("lord", StringUtils.between("hellord", "l", null));
    }
    
    @Test
    public void testBetween() {
        assertEquals("", StringUtils.between("", "", ""));
        assertEquals("", StringUtils.between("hellord", "a", "rd"));
        assertEquals("", StringUtils.between("hellord", "he", "q"));
        
        assertEquals("llo", StringUtils.between("hellord", "he", "rd"));
        assertEquals("l", StringUtils.between("hellord", "el", "ord"));
        
        assertEquals("", StringUtils.between("hello world", "l", "l"));
        assertEquals(" w", StringUtils.between("hello world", "o", "o"));
    }
	
    @Test
    public void testRaw() {
        assertEquals("some unique", StringUtils.raw(/*some unique*/));
        assertEquals("hello world!", StringUtils.raw(/*hello world!*/));
        assertEquals("", StringUtils.raw(/**/));
        assertEquals("\"hello\\world\"", StringUtils.raw(/*"hello\world"*/));
        assertEquals("a\r\nb\r\nc", StringUtils.raw(
/*a
b
c*/
        ));
    }
    
    @Test
    public void testWrap() { //NOPMD assert
        StringTester.assertString("Hel", StringUtils.wrap("Hel".toCharArray()));
        StringTester.assertString("Hel", StringUtils.wrap("Hello".toCharArray(),0,3));
        StringTester.assertString("l" , StringUtils.wrap("Hello".toCharArray(),2,3));
        StringTester.assertString("llo", StringUtils.wrap("Hello".toCharArray(),2,5));
        
        StringTester.assertString("ello wo", StringUtils.wrap("Hello world".toCharArray()).subSequence(1, 8) );
    }
    
    @Test
    public void testRepeatChar() { //NOPMD assert
        StringTester.assertString("...", StringUtils.repeat(3,'.'));
        StringTester.assertString(".....", StringUtils.repeat(16,'.').subSequence(3, 8));
    }
    
    @Test
    public void testRepeatString() { //NOPMD assert
        StringTester.assertString("rerere", StringUtils.repeat(3,"re"));
        StringTester.assertString("arvarvar", StringUtils.repeat(4,"var").subSequence(4, 12));
        StringTester.assertString("arvarvarv", StringUtils.repeat(5,"var").subSequence(4, 13));
    }
    
    @Test
    public void testIndexOf() {
        assertEquals(0, StringUtils.indexOf("hel", "hel"));
        assertEquals(0, StringUtils.indexOf("hello", "hel"));
        
        assertEquals(0, StringUtils.indexOf("hello", ""));
        assertEquals(0, StringUtils.indexOf("", ""));
        assertTrue( StringUtils.indexOf("", "q")<0 );
        
        assertEquals(5, StringUtils.indexOf("hello world", " w"));
        assertEquals(5, StringUtils.indexOf("hello world", " "));
        
        assertEquals(10, StringUtils.indexOf("hello world", "d"));
        assertEquals(8, StringUtils.indexOf("hello world", "rld"));
        assertEquals(2, StringUtils.indexOf("hello world", "l"));
        
        assertTrue( StringUtils.indexOf("hello world", "q")<0 );
        assertTrue( StringUtils.indexOf("hello world", "we")<0 );
        assertTrue( StringUtils.indexOf("hello world", "lda")<0 );
    }
    
    @Test
    public void indexOf_offset() {
        assertEquals(0, StringUtils.indexOf("her her uk", "her"));
        assertEquals(4, StringUtils.indexOf("her her uk", "her", 1));
    }
    
    @Test
    public void testContains() {
        assertTrue(StringUtils.contains("hel", "hel"));
        assertTrue(StringUtils.contains("hello", "hel"));
        
        assertTrue(StringUtils.contains("hello", ""));
        assertTrue(StringUtils.contains("", ""));
        assertFalse(StringUtils.contains("", "q"));
        
        assertTrue(StringUtils.contains("hello world", " w"));
        assertTrue(StringUtils.contains("hello world", " "));
        
        assertTrue(StringUtils.contains("hello world", "d"));
        assertTrue(StringUtils.contains("hello world", "rld"));
        assertTrue(StringUtils.contains("hello world", "l"));
        
        assertFalse(StringUtils.contains("hello world", "q"));
        assertFalse(StringUtils.contains("hello world", "we"));
        assertFalse(StringUtils.contains("hello world", "lda"));
    }
    
    @Test
    public void testSize() {
        assertEquals(0, StringUtils.length(null));
        assertEquals(0, StringUtils.length(""));
        assertEquals(1, StringUtils.length(" "));
        assertEquals(5, StringUtils.length("hello"));
    }
    
    @Test
    public void testCodePoints() {
        assertEquals("Hello world", StringUtils.fromCodePoints(StringUtils.toCodePoints("Hello world")));
    }
    
    @Test
    public void testSwapcase() {
        assertEquals("Hello wORLD!", StringUtils.swapcase("hELLO World!"));
    }

    @Test
    public void testLimit() throws Exception {
        assertLimitEquals("Hello", StringUtils.limit("Hello world", 5));
        assertLimitEquals("Hello", StringUtils.limit("Hello", 10));
    }

    private static void assertLimitEquals(String expected, CharSequence value) {
	    assertEquals(expected, value.toString());
	    assertEquals(expected.length(), value.length());
	    for(int i=0, n=expected.length(); i<n; i++) {
	        assertEquals(expected.charAt(i), value.charAt(i));
        }
    }
}
