/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.soteradefense.dga.io.formats;

import org.apache.giraph.conf.GiraphConfiguration;
import org.apache.giraph.conf.ImmutableClassesGiraphConfiguration;
import org.apache.giraph.graph.BasicComputation;
import org.apache.giraph.io.EdgeReader;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.io.IOException;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@SuppressWarnings("unchecked")
public class DGALongEdgeValueInputFormatTest extends DGALongEdgeValueInputFormat {

    private RecordReader<LongWritable,Text> rr;
    private ImmutableClassesGiraphConfiguration<Text, Text, Text> conf;
    private TaskAttemptContext tac;

    @Before
    public void setUp() throws IOException, InterruptedException {
        rr = mock(RecordReader.class);
        GiraphConfiguration giraphConf = new GiraphConfiguration();
        giraphConf.setComputationClass(BasicComputation.class);
        giraphConf.set(DGALongEdgeValueInputFormat.LINE_TOKENIZE_VALUE, ",");
        conf = new ImmutableClassesGiraphConfiguration<Text, Text, Text>(giraphConf);
        tac = mock(TaskAttemptContext.class);
        when(tac.getConfiguration()).thenReturn(conf);
    }

    public EdgeReader<Text, LongWritable> createEdgeReader(final RecordReader<LongWritable,Text> rr) throws IOException {
        return new DGALongEdgeValueReader(){
            @Override
            protected RecordReader<LongWritable, Text> createLineRecordReader(InputSplit inputSplit, TaskAttemptContext context) throws IOException, InterruptedException {
                return rr;
            }
        };
    }

    @Test
    public void testInputParserWithDefaultWeight() throws IOException, InterruptedException {
        String input = "1,2";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text("1"));
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text("2"));
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(1L));

    }

    @Test
    public void testInputParserWithDefaultWeightAndOverriddenSeparator() throws IOException, InterruptedException {
        String input = "1\t2";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        conf.set(LINE_TOKENIZE_VALUE, "\t");
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text("1"));
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text("2"));
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(1L));

    }

    @Test
    public void testInputParserWithCustomWeight() throws IOException, InterruptedException {
        String input = "1,2,10";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text("1"));
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text("2"));
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(10L));

    }
    @Test
    public void testInputParserWithCustomWeightAndOverriddenSeparator() throws IOException, InterruptedException {
        String input = "1\t2\t10";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        conf.set(LINE_TOKENIZE_VALUE, "\t");
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text("1"));
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text("2"));
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(10L));

    }

    @Test
    public void testInputParserWithDelimiterInData() throws IOException, InterruptedException {
        String input = "te\\[email protected]\[email protected]\t10";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        conf.set(LINE_TOKENIZE_VALUE, "\t");
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text("te\\[email protected]"));
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text("[email protected]"));
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(10L));

    }
    @Test(expected=IOException.class)
    public void testInputParserWithDelimiterInDataNoEscape() throws IOException, InterruptedException {
        String input = "te\[email protected]\[email protected]\t10";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        conf.set(LINE_TOKENIZE_VALUE, "\t");
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text("te\\[email protected]"));
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text("[email protected]"));
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(10L));

    }
    @Test(expected=IOException.class)
    public void testInputParserWithMalformedLine() throws IOException, InterruptedException {
        String input = "1";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text("1"));
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text());
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(1L));

    }
    @Test(expected=IOException.class)
    public void testInputParserWithMalformedLineAndDelimiter() throws IOException, InterruptedException {
        String input = "1,";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text("1"));
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text());
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(1L));

    }
    @Test(expected=IOException.class)
    public void testInputParserWithMalformedLineAndDelimiterNoSource() throws IOException, InterruptedException {
        String input = ",1";
        when(rr.getCurrentValue()).thenReturn(new Text(input));
        EdgeReader ter = createEdgeReader(rr);
        ter.setConf(conf);
        ter.initialize(null, tac);
        assertEquals(ter.getCurrentSourceId(), new Text());
        assertEquals(ter.getCurrentEdge().getTargetVertexId(), new Text("1"));
        assertEquals(ter.getCurrentEdge().getValue(), new LongWritable(1L));

    }
}