package com.ffbit.bencode;

import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.NoSuchElementException;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

public class BDecoderTest {
    private BDecoder decoder;

    @Test
    public void itShouldParseListOfIntegers() throws Exception {
        InputStream in = new ByteArrayInputStream("li1ei2ee".getBytes());
        decoder = new BDecoder(in);

        assertThat(decoder.decode(), is((Object) asList(1, 2)));
    }

    @Test
    public void itShouldDecodeListOfStringAndInteger() throws Exception {
        InputStream in = new ByteArrayInputStream("li1e1:ae".getBytes());
        decoder = new BDecoder(in);

        assertThat(decoder.decode(), is((Object) asList(1, "a")));
    }

    @Test
    public void itShouldDecodeListOfStringAndIntegerAndNestedList() throws Exception {
        InputStream in = new ByteArrayInputStream("li1e1:ali2eee".getBytes());
        decoder = new BDecoder(in);

        assertThat(decoder.decode(), is((Object) asList(1, "a", asList(2))));
    }

    @Test
    public void itShouldDecodeDictionaryOfSingleIntegerValue() throws Exception {
        InputStream in = new ByteArrayInputStream("d1:ai1ee".getBytes());
        decoder = new BDecoder(in);

        assertThat(decoder.decode(), is((Object) singletonMap("a", 1)));
    }

    @Test
    public void itShouldDecodeDictionaryOfMultipleIntegerValues() throws Exception {
        InputStream in = new ByteArrayInputStream("d1:ai2e3:fooi42ee".getBytes());
        decoder = new BDecoder(in);
        HashMap<String, Object> expected = new HashMap<String, Object>() {{
            put("a", 2);
            put("foo", 42);
        }};

        assertThat(decoder.decode(), is((Object) expected));
    }

    @Test
    public void itShouldDecodeDictionaryOfMultipleIntegerValuesAndNestedList() throws Exception {
        InputStream in = new ByteArrayInputStream("d1:ai2e3:fooi42e1:lli1eee".getBytes());
        decoder = new BDecoder(in);
        HashMap<String, Object> expected = new HashMap<String, Object>() {{
            put("a", 2);
            put("foo", 42);
            put("l", asList(1));
        }};

        assertThat(decoder.decode(), is((Object) expected));
    }

    @Test
    public void itShouldBeIterable() throws Exception {
        InputStream in = new ByteArrayInputStream("1:ai42eld3:key5:valueee".getBytes());
        decoder = new BDecoder(in);

        assertThat(decoder, hasItems("a", 42, singletonList(singletonMap("key", "value"))));
    }

    @Test(expected = UnsupportedOperationException.class)
    public void itShouldNotSupportRemovingOfElements() throws Exception {
        decoder = new BDecoder(null);

        decoder.remove();
    }

    @Test
    public void itShouldHaveNoElementsLeft() throws Exception {
        decoder = new BDecoder(new ByteArrayInputStream(new byte[]{}));

        assertThat(decoder.hasNext(), is(false));
    }

    @Test(expected = NoSuchElementException.class)
    public void itShouldThrowNoSuchElementExceptionWhenNoElementsLeft() throws Exception {
        decoder = new BDecoder(new ByteArrayInputStream(new byte[]{}));

        assertThat(decoder.hasNext(), is(false));
        decoder.next();
    }

}

@@ -74,32 +74,60 @@
 rayInputStream;%0A
+import java.io.IOException;%0A
 import java.io.I
@@ -482,16 +482,153 @@
 ertThat;
+%0Aimport static org.mockito.Mockito.mock;%0Aimport static org.mockito.Mockito.when;%0A%0Aimport static com.ffbit.bencode.Encoder.INTEGER_PREFIX;
 %0A%0Apublic
@@ -3686,10 +3686,1038 @@
 %0A    %7D%0A%0A
+    @Test%0A    public void itShouldNotHaveNextOnIOException() throws Exception %7B%0A        InputStream in = mock(InputStream.class);%0A        decoder = new BDecoder(in);%0A        when(in.read()).thenThrow(IOException.class);%0A%0A        assertThat(decoder.hasNext(), is(false));%0A    %7D%0A%0A    @Test(expected = BDecoderException.class)%0A    public void itShouldThrowBDecoderExceptionWhenUnsupportedByteOccurred() throws Exception %7B%0A        decoder = new BDecoder(new ByteArrayInputStream(%22a%22.getBytes()));%0A%0A        assertThat(decoder.hasNext(), is(true));%0A        decoder.next();%0A    %7D%0A%0A    @Test(expected = BDecoderException.class)%0A    public void itShouldWrapIOExceptionToBDecoderException() throws Exception %7B%0A        InputStream in = mock(InputStream.class);%0A        decoder = new BDecoder(in);%0A        when(in.read())%0A                .thenReturn((int) INTEGER_PREFIX)%0A                .thenReturn((int) '4')%0A                .thenThrow(IOException.class);%0A%0A        assertThat(decoder.hasNext(), is(true));%0A%0A        decoder.next();%0A    %7D%0A%0A
 %7D%0A

Increase test coverage of the BDecoder.