Skip to content

Instantly share code, notes, and snippets.

@i-takehiro
Created March 23, 2013 01:33
Show Gist options
  • Save i-takehiro/5225990 to your computer and use it in GitHub Desktop.
Save i-takehiro/5225990 to your computer and use it in GitHub Desktop.
TDDBC Tokyo 2013-03 のお題にチャレンジしてみた (お題の参考)https://github.com/sue445/tddbc_tokyo_20130316 ⇒お題の1枚目、2枚目まで実装
package ltsv;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* TDDBC Tokyo 2013-03
* お題 (1枚目、2枚目まで実装)
* https://github.com/sue445/tddbc_tokyo_20130316
*/
public class LTSV {
private static final char NEWLINE = '\n';
private static final char TAB = '\t';
private static final char COLON = ':';
private static final String EMPTY_LINE = "" + NEWLINE;
private final LinkedHashMap<String, String> values;
public LTSV() {
// 挿入順を保持するため LinkedHashMap を使用する
this.values = new LinkedHashMap<String, String>();
}
public String dump() {
if (this.values.isEmpty()) {
return EMPTY_LINE;
}
final StringBuilder builder = new StringBuilder();
boolean isFirst = true;
for (Map.Entry<String, String> entry : values.entrySet()) {
if (isFirst == false) {
builder.append(TAB);
}
final String key = entry.getKey();
final String value = entry.getValue();
builder.append(escape(key)).append(COLON).append(escape(value));
isFirst = false;
}
builder.append(NEWLINE);
return builder.toString();
}
public String get(final String key) {
isNotNull(key, "key must not be null.");
isNotEmpty(key, "key must not be empty.");
return this.values.get(key);
}
public String set(final String key, final String value) {
isNotNull(key, "key must not be null.");
isNotEmpty(key, "key must not be empty.");
isNotNull(value, "value must not be null.");
// キーをマップに「再挿入」する場合は挿入順に影響を受けないので、明示的に削除と挿入を行う
final String oldValue = this.values.remove(key);
this.values.put(key, value);
return oldValue;
}
private String escape(final String value) {
if (value == null || value.length() == 0) {
return value;
}
final int length = value.length();
final StringBuilder escaped = new StringBuilder(length * 2);
for (int i = 0; i < length; i++) {
char c = value.charAt(i);
switch (c) {
case COLON:
escaped.append("\\:");
break;
case TAB:
escaped.append("\\t");
break;
case NEWLINE:
escaped.append("\\n");
break;
default:
escaped.append(c);
break;
}
}
return escaped.toString();
}
private <T> void isNotNull(final T object, final String message) {
isTrue(object != null, message);
}
private void isNotEmpty(final CharSequence chars, final String message) {
isTrue(0 < chars.length(), message);
}
private void isTrue(final boolean condition, final String message) {
if (condition == false) {
throw new IllegalArgumentException(message);
}
}
}
package ltsv;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
@RunWith(Enclosed.class)
public class LTSVTest {
public static class getの例外条件のテスト {
private LTSV sut;
@Rule
public ExpectedException expected = ExpectedException.none();
@Before
public void setUp() throws Exception {
sut = new LTSV();
expected.expect(IllegalArgumentException.class);
}
@Test
public void nullキーは例外発生() throws Exception {
// SetUp
// Exercise
sut.get(null);
// Verify
}
@Test
public void 空文字キーは例外発生() throws Exception {
// SetUp
// Exercise
sut.get("");
// Verify
}
}
public static class setの例外条件のテスト {
// ltsv.set( nil , "momo" ) # nullキーは例外発生
// ltsv.set( "" , "gogo" ) # 空文字キーは例外発生
// ltsv.set( "key", null ) # null値は例外発生
// ltsv.set( "key", "" ) # 空文字値はOK -> dumpは?
private LTSV sut;
@Rule
public ExpectedException expected = ExpectedException.none();
@Before
public void setUp() throws Exception {
sut = new LTSV();
}
@Test
public void nullキーは例外発生() throws Exception {
// SetUp
expected.expect(IllegalArgumentException.class);
// Exercise
sut.set(null, "momo");
// Verify
}
@Test
public void 空文字列キーは例外発生() throws Exception {
// SetUp
expected.expect(IllegalArgumentException.class);
// Exercise
sut.set("", "gogo");
// Verify
}
@Test
public void null値は例外発生() throws Exception {
// SetUp
expected.expect(IllegalArgumentException.class);
// Exercise
sut.set("key", null);
// Verify
}
@Test
public void 空文字値はOK() throws Exception {
// SetUp
// Exercise
sut.set("key", "");
// Verify
}
}
public static class データが存在しない {
private LTSV sut;
@Before
public void setUp() throws Exception {
sut = new LTSV();
}
@Test
public void dumpすると改行のみが返る() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.dump(), is("\n"));
}
@Test
public void 存在しないキーでsetするとnullが返される() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.set("foo", "hoge"), nullValue());
}
@Test
public void 存在しないキーの値はnull() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.get("toto"), nullValue());
}
}
public static class データが1つsetされている {
private LTSV sut;
@Before
public void setUp() throws Exception {
sut = new LTSV();
sut.set("foo", "hoge");
}
@Test
public void set済みのキーでgetすると値が返される() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.get("foo"), is("hoge"));
}
@Test
public void dump() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.dump(), is("foo:hoge\n"));
}
@Test
public void 新しいキーでsetするとnullが返される() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.set("bar", "fuga"), nullValue());
}
}
public static class データが2つsetされている {
private LTSV sut;
@Before
public void setUp() throws Exception {
sut = new LTSV();
sut.set("foo", "hoge");
sut.set("bar", "fuga");
}
@Test
public void fooでgetするとhogeが返される() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.get("foo"), is("hoge"));
}
@Test
public void barでgetするとfugaが返される() throws Exception {
// SetUp
// Exercise
// Verify
// (コメント) お題の1枚目に以下のコードもあると良い?(getの返り値がキーの値に関係なく最後にsetした値と誤解する?)
// ltsv.get( "bar" ) #=> fuga
assertThat(sut.get("bar"), is("fuga"));
}
@Test
public void 格納順にdumpされる() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.dump(), is("foo:hoge\tbar:fuga\n"));
}
@Test
public void 設定済みのキーでsetすると設定済みの値が返される() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.set("foo", "piyo"), is("hoge"));
}
}
public static class 同じキーで2回以上値がセットされている場合 {
private LTSV sut;
@Before
public void setUp() throws Exception {
sut = new LTSV();
sut.set("foo", "hoge");
sut.set("bar", "fuga");
sut.set("foo", "piyo");
}
@Test
public void 後からsetした値が取得される() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.get("foo"), is("piyo"));
}
@Test
public void 格納順にdumpされる() throws Exception {
// SetUp
// Exercise
// Verify
assertThat(sut.dump(), is("bar:fuga\tfoo:piyo\n"));
}
}
public static class データに特殊文字が含まれる場合のdump {
private LTSV sut;
@Before
public void setUp() throws Exception {
sut = new LTSV();
}
@Test
public void キーにコロンが含まれる場合バックスラッシュでエスケープ() throws Exception {
// SetUp
sut.set("key1:key2", "value");
// Exercise
// Verify
assertThat(sut.dump(), is("key1\\:key2:value\n"));
}
@Test
public void キーにタブが含まれる場合バックスラッシュでエスケープ() throws Exception {
// SetUp
sut.set("key1\tkey2", "value");
// Exercise
// Verify
assertThat(sut.dump(), is("key1\\tkey2:value\n"));
}
@Test
public void キーに改行が含まれる場合バックスラッシュでエスケープ() throws Exception {
// SetUp
sut.set("key1\nkey2", "value");
// Exercise
// Verify
assertThat(sut.dump(), is("key1\\nkey2:value\n"));
}
@Test
public void キーにコロン_タブ_改行が混在したときに全てエスケープ() throws Exception {
// SetUp
sut.set("key1\tkey2\nkey3:key4", "value");
// Exercise
// Verify
assertThat(sut.dump(), is("key1\\tkey2\\nkey3\\:key4:value\n"));
}
@Test
public void 値が空文字列の場合は空で出力() throws Exception {
// SetUp
sut.set("foo", "");
// Exercise
// Verify
assertThat(sut.dump(), is("foo:\n"));
}
@Test
public void 値にコロンが含まれる場合バックスラッシュでエスケープ() throws Exception {
// SetUp
sut.set("foo", "value1:value2");
// Exercise
// Verify
assertThat(sut.dump(), is("foo:value1\\:value2\n"));
}
@Test
public void 値にタブが含まれる場合バックスラッシュでエスケープ() throws Exception {
// SetUp
sut.set("foo", "value1\tvalue2");
// Exercise
// Verify
assertThat(sut.dump(), is("foo:value1\\tvalue2\n"));
}
@Test
public void 値に改行が含まれる場合バックスラッシュでエスケープ() throws Exception {
// SetUp
sut.set("foo", "value1\nvalue2");
// Exercise
// Verify
assertThat(sut.dump(), is("foo:value1\\nvalue2\n"));
}
@Test
public void 値にコロン_タブ_改行が混在したときに全てエスケープ() throws Exception {
// SetUp
sut.set("foo", "aaa\nbbb\tccc:ddd");
// Exercise
// Verify
assertThat(sut.dump(), is("foo:aaa\\nbbb\\tccc\\:ddd\n"));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment