toml4j/src/test/java/com/moandjiezana/toml/TomlWriterTest.java

576 lines
16 KiB
Java

package com.moandjiezana.toml;
import static org.junit.Assert.*;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.RetentionPolicy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
@SuppressWarnings("unused")
public class TomlWriterTest {
@Rule
public TemporaryFolder testDirectory = new TemporaryFolder();
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void should_write_primitive_types() {
class TestClass {
public String aString = "hello";
int anInt = 4;
protected float aFloat = 1.23f;
private double aDouble = -5.43;
final boolean aBoolean = false;
static final int aFinalInt = 1; // Should be skipped
Date aDate;
}
TestClass o = new TestClass();
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Africa/Johannesburg"));
calendar.set(2015, Calendar.JULY, 1, 11, 5, 30);
calendar.set(Calendar.MILLISECOND, 0);
o.aDate = calendar.getTime();
String output = new TomlWriter().write(o);
String expected = "aString = \"hello\"\n" +
"anInt = 4\n" +
"aFloat = 1.23\n" +
"aDouble = -5.43\n" +
"aBoolean = false\n" +
"aDate = 2015-07-01T09:05:30Z\n";
assertEquals(expected, output);
}
class SubChild {
int anInt;
}
class Child {
SubChild subChild;
int anInt;
}
class Parent {
Map<String, Object> aMap;
Child child;
boolean aBoolean;
}
private Parent buildNestedMap() {
Parent parent = new Parent();
parent.aMap = new LinkedHashMap<String, Object>();
parent.aMap.put("foo", 1);
parent.aMap.put("bar", "value1");
parent.aMap.put("baz.x", true);
parent.child = new Child();
parent.child.anInt = 2;
parent.child.subChild = new SubChild();
parent.child.subChild.anInt = 4;
parent.aBoolean = true;
return parent;
}
@Test
public void should_write_nested_map_with_default_indentation_policy() {
String output = new TomlWriter().write(buildNestedMap());
String expected = "aBoolean = true\n\n" +
"[aMap]\n" +
"foo = 1\n" +
"bar = \"value1\"\n" +
"\"baz.x\" = true\n\n" +
"[child]\n" +
"anInt = 2\n\n" +
"[child.subChild]\n" +
"anInt = 4\n";
assertEquals(expected, output);
}
@Test
public void should_follow_indentation_policy_of_indented_values() {
String output = new TomlWriter.Builder().
indentValuesBy(2).
build().
write(buildNestedMap());
String expected = "aBoolean = true\n\n" +
"[aMap]\n" +
" foo = 1\n" +
" bar = \"value1\"\n" +
" \"baz.x\" = true\n\n" +
"[child]\n" +
" anInt = 2\n\n" +
"[child.subChild]\n" +
" anInt = 4\n";
assertEquals(expected, output);
}
@Test
public void should_follow_indentation_policy_of_indented_tables() {
String output = new TomlWriter.Builder().
indentTablesBy(2).
build().
write(buildNestedMap());
String expected = "aBoolean = true\n\n" +
"[aMap]\n" +
"foo = 1\n" +
"bar = \"value1\"\n" +
"\"baz.x\" = true\n\n" +
"[child]\n" +
"anInt = 2\n\n" +
" [child.subChild]\n" +
" anInt = 4\n";
assertEquals(expected, output);
}
@Test
public void should_follow_indentation_policy_of_indented_tables_and_values() {
String output = new TomlWriter.Builder().
indentValuesBy(2).
indentTablesBy(2).
build().
write(buildNestedMap());
String expected = "aBoolean = true\n\n" +
"[aMap]\n" +
" foo = 1\n" +
" bar = \"value1\"\n" +
" \"baz.x\" = true\n\n" +
"[child]\n" +
" anInt = 2\n\n" +
" [child.subChild]\n" +
" anInt = 4\n";
assertEquals(expected, output);
}
@Test
public void should_write_array_of_tables_from_object() {
class Table {
int anInt;
Table(int anInt) {
this.anInt = anInt;
}
}
class Config {
Table[] table = new Table[]{new Table(1), new Table(2)};
List<Table> table2 = Arrays.asList(new Table(3), new Table(4));
}
Config config = new Config();
String output = new TomlWriter().write(config);
String expected = "[[table]]\n" +
"anInt = 1\n\n" +
"[[table]]\n" +
"anInt = 2\n" +
"[[table2]]\n" +
"anInt = 3\n\n" +
"[[table2]]\n" +
"anInt = 4\n";
assertEquals(expected, output);
}
@Test
public void should_write_array_of_tables_from_map() throws Exception {
List<Map<String, Object>> maps = new ArrayList<Map<String,Object>>();
HashMap<String, Object> item1 = new HashMap<String, Object>();
item1.put("anInt", 1L);
HashMap<String, Object> item2 = new HashMap<String, Object>();
item2.put("anInt", 2L);
maps.add(item1);
maps.add(item2);
Map<String, Object> input = new HashMap<String, Object>();
input.put("maps", maps);
String output = new TomlWriter().write(input);
String expected = "[[maps]]\n" +
"anInt = 1\n\n" +
"[[maps]]\n" +
"anInt = 2\n";
assertEquals(expected, output);
}
@Test
public void should_write_array_of_array() {
class ArrayTest {
int[][] array = {{1, 2, 3}, {4, 5, 6}};
}
ArrayTest arrayTest = new ArrayTest();
String output = new TomlWriter.Builder().padArrayDelimitersBy(1).build().write(arrayTest);
String expected = "array = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]\n";
assertEquals(expected, output);
}
@Test
public void should_write_list() {
class ListTest {
List<Integer> aList = new LinkedList<Integer>();
}
ListTest o = new ListTest();
o.aList.add(1);
o.aList.add(2);
assertEquals("aList = [ 1, 2 ]\n", new TomlWriter.Builder().padArrayDelimitersBy(1).build().write(o));
}
@Test
public void should_handle_zero_length_arrays_and_lists() {
class TestClass {
List<Integer> aList = new LinkedList<Integer>();
Float[] anArray = new Float[0];
}
assertEquals("aList = []\nanArray = []\n", new TomlWriter().write(new TestClass()));
}
@Test
public void should_reject_heterogeneous_arrays() {
class BadArray {
Object[] array = new Object[2];
}
BadArray badArray = new BadArray();
badArray.array[0] = new Integer(1);
badArray.array[1] = "oops";
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(Matchers.startsWith("array"));
new TomlWriter().write(badArray);
}
@Test
public void should_reject_nested_heterogeneous_array() {
class BadArray {
Map<String, Object> aMap = new HashMap<String, Object>();
}
BadArray badArray = new BadArray();
badArray.aMap.put("array", new Object[] { Integer.valueOf(1), "oops" });
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("aMap.array");
new TomlWriter().write(badArray);
}
@Test
public void should_elide_empty_intermediate_tables() {
class C {
int anInt = 1;
}
class B {
C c = new C();
}
class A {
B b = new B();
}
assertEquals("[b.c]\nanInt = 1\n", new TomlWriter().write(new A()));
}
@Test
public void should_write_map() throws Exception {
assertEquals("a = 1\n", new TomlWriter().write(new Toml().read("a = 1").toMap()));
}
class Base {
protected int anInt = 2;
}
class Impl extends Base {
boolean aBoolean = true;
}
@Test
public void should_write_classes_with_inheritance() {
Impl impl = new Impl();
String expected = "aBoolean = true\nanInt = 2\n";
assertEquals(expected, new TomlWriter().write(impl));
}
@Test
public void should_write_strings_to_toml_utf8() throws UnsupportedEncodingException {
class Utf8Test {
String input;
}
Utf8Test utf8Test = new Utf8Test();
utf8Test.input = " é foo \u20AC \b \t \n \f \r \" \\ ";
assertEquals("input = \" é foo € \\b \\t \\n \\f \\r \\\" \\\\ \"\n", new TomlWriter().write(utf8Test));
// Check unicode code points greater than 0XFFFF
utf8Test.input = " \uD801\uDC28 \uD840\uDC0B ";
assertEquals("input = \" 𐐨 𠀋 \"\n", new TomlWriter().write(utf8Test));
}
@Test
public void should_quote_keys() {
Map<String, Integer> aMap = new LinkedHashMap<String, Integer>();
aMap.put("a.b", 1);
aMap.put("5€", 2);
aMap.put("c$d", 3);
aMap.put("e/f", 4);
String expected = "\"a.b\" = 1\n" +
"\"5€\" = 2\n" +
"\"c$d\" = 3\n" +
"\"e/f\" = 4\n";
assertEquals(expected, new TomlWriter().write(aMap));
}
@Test
public void should_quote_keys_in_object() throws Exception {
class A$ {
Double µµ = 5.3;
}
class A {
int 5 = 5;
String français = "langue";
A$ a$ = new A$();
}
assertEquals("\"€5\" = 5\n\"français\" = \"langue\"\n\n[\"a$\"]\n\"µµ\" = 5.3\n", new TomlWriter().write(new A()));
}
@Test
public void should_handle_urls() throws Exception {
class WithUrl {
URL url;
URI uri;
}
WithUrl from = new WithUrl();
from.url = new URL("https://github.com");
from.uri = new URI("https://bitbucket.com");
String expected = "url = \"https://github.com\"\n"
+ "uri = \"https://bitbucket.com\"\n";
assertEquals(expected, new TomlWriter().write(from));
}
@Test
public void should_handle_enum() throws Exception {
class WithEnum {
RetentionPolicy retentionPolicy = RetentionPolicy.RUNTIME;
}
assertEquals("retentionPolicy = \"RUNTIME\"\n", new TomlWriter().write(new WithEnum()));
}
@Test
public void should_handle_char() throws Exception {
class WithChar {
char c = 'a';
}
assertEquals("c = \"a\"\n", new TomlWriter().write(new WithChar()));
}
@Test
public void should_handle_big_numbers() throws Exception {
class WithBigNumbers {
BigInteger bigInt = BigInteger.valueOf(1);
BigDecimal bigDecimal = BigDecimal.valueOf(2.8);
}
String expected = "bigInt = 1\n"
+ "bigDecimal = 2.8\n";
assertEquals(expected, new TomlWriter().write(new WithBigNumbers()));
}
@Test
public void should_handle_wrappers() throws Exception {
class WithWrappers {
Character c = Character.valueOf('b');
Long l = Long.valueOf(2);
Double d = Double.valueOf(3.4);
}
String expected = "c = \"b\"\n"
+ "l = 2\n"
+ "d = 3.4\n";
assertEquals(expected, new TomlWriter().write(new WithWrappers()));
}
@Test
public void should_use_specified_time_zone() throws Exception {
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.set(2015, Calendar.JULY, 1, 11, 5, 30);
calendar.set(Calendar.MILLISECOND, 0);
Map<String, Date> o = new HashMap<String, Date>();
o.put("sast", calendar.getTime());
TomlWriter writer = new TomlWriter.Builder().
timeZone(TimeZone.getTimeZone("Africa/Johannesburg")).
build();
assertEquals("sast = 2015-07-01T13:05:30+02:00\n", writer.write(o));
}
@Test
public void should_show_fractional_seconds() throws Exception {
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.set(2015, Calendar.JULY, 1, 11, 5, 30);
calendar.set(Calendar.MILLISECOND, 345);
Map<String, Date> o = new HashMap<String, Date>();
o.put("date", calendar.getTime());
TomlWriter writer = new TomlWriter.Builder().
showFractionalSeconds().
build();
assertEquals("date = 2015-07-01T11:05:30.345Z\n", writer.write(o));
}
@Test
public void should_show_fractional_seconds_in_specified_time_zone() throws Exception {
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.set(2015, Calendar.JULY, 1, 11, 5, 30);
calendar.set(Calendar.MILLISECOND, 345);
Map<String, Date> o = new LinkedHashMap<String, Date>();
o.put("date", calendar.getTime());
calendar.set(Calendar.MINUTE, 37);
o.put("date2", calendar.getTime());
TomlWriter writer = new TomlWriter.Builder().
timeZone(TimeZone.getTimeZone("Africa/Johannesburg")).
showFractionalSeconds().
build();
String expected = "date = 2015-07-01T13:05:30.345+02:00\n"
+ "date2 = 2015-07-01T13:37:30.345+02:00\n";
assertEquals(expected, writer.write(o));
}
private static class SimpleTestClass {
int a = 1;
}
private static class TransientClass {
int a = 2;
transient int b = 3;
}
@Test
public void should_write_to_writer() throws IOException {
StringWriter output = new StringWriter();
new TomlWriter().write(new SimpleTestClass(), output);
assertEquals("a = 1\n", output.toString());
}
@Test
public void should_write_to_outputstream() throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
new TomlWriter().write(new SimpleTestClass(), output);
assertEquals("a = 1\n", output.toString());
}
@Test
public void should_write_to_file() throws IOException {
File output = testDirectory.newFile();
new TomlWriter().write(new SimpleTestClass(), output);
assertEquals("a = 1\n", readFile(output));
}
@Test
public void should_skip_transient_fields() throws Exception {
String toml = new TomlWriter().write(new TransientClass());
assertEquals("a = 2\n", toml);
}
@Test(expected = IllegalArgumentException.class)
public void should_refuse_to_write_string_fragment() {
new TomlWriter().write("fragment");
}
@Test(expected = IllegalArgumentException.class)
public void should_refuse_to_write_boolean_fragment() {
new TomlWriter().write(true);
}
@Test(expected = IllegalArgumentException.class)
public void should_refuse_to_write_number_fragment() {
new TomlWriter().write(42);
}
@Test(expected = IllegalArgumentException.class)
public void should_refuse_to_write_date_fragment() {
new TomlWriter().write(new Date());
}
@Test(expected = IllegalArgumentException.class)
public void should_refuse_to_write_array_fragment() {
new TomlWriter().write(new int[2]);
}
@Test(expected = IllegalArgumentException.class)
public void should_refuse_to_write_table_array_fragment() {
new TomlWriter().write(new SimpleTestClass[2]);
}
@Test(expected=IllegalArgumentException.class)
public void should_not_write_list() throws Exception {
new TomlWriter().write(Arrays.asList("a"));
}
private String readFile(File input) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader(input));
try {
StringBuilder w = new StringBuilder();
String line = bufferedReader.readLine();
while (line != null) {
w.append(line).append('\n');
line = bufferedReader.readLine();
}
return w.toString();
} finally {
bufferedReader.close();
}
}
}