/*
 * Copyright (c) 2011-2017 Contributors to the Eclipse Foundation
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
 * which is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
 */

package io.vertx.core.http;

import io.vertx.core.MultiMap;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;

public class CaseInsensitiveHeadersTest {

  // Same hash
  protected String sameHash1 = "AZ";
  protected String sameHash2 = "\u0080Y";

  // Different hash / same bucket
  protected String sameBucket1 = "A";
  protected String sameBucket2 = "R";

  protected MultiMap newMultiMap() {
    return new CaseInsensitiveHeaders();
  }

  @Test
  public void checkNameCollision() {
    assertEquals(hash(sameHash1), hash(sameHash2));
    assertNotEquals(hash(sameBucket1), hash(sameBucket2));
    assertEquals(index(hash(sameBucket1)), index(hash(sameBucket2)));
  }

  @Test
  public void testCaseInsensitiveHeaders()
      throws Exception {

    MultiMap result = newMultiMap();

    assertNotNull(result);
    assertTrue(result.isEmpty());
    assertEquals(0, result.size());
    assertEquals("", result.toString());
  }

  @Test
  public void testAddTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("a", "b");

    MultiMap result = mmap.addAll(map);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals("a: b\n", result.toString());
  }

  @Test
  public void testAddTest2()
      throws Exception {
    MultiMap mmap = newMultiMap();
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("a", "b");
    map.put("c", "d");

    assertEquals("a: b\nc: d\n", mmap.addAll(map).toString());
  }

  @Test
  public void testAddTest3()
      throws Exception {
    MultiMap mmap = newMultiMap();
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("a", "b");

    assertEquals("a: b\n", mmap.addAll(map).toString());
  }

  @Test
  public void testAddTest4()
      throws Exception {
    MultiMap mmap = newMultiMap();
    Map<String, String> map = new HashMap<String, String>();

    assertEquals("", mmap.addAll(map).toString());
  }

  @Test
  public void testAddTest5()
      throws Exception {
    MultiMap mmap = newMultiMap();
    MultiMap headers = newMultiMap();

    assertEquals("", mmap.addAll(headers).toString());
  }

  @Test
  public void testAddTest7()
      throws Exception {
    MultiMap mmap = newMultiMap();
    CharSequence name = "name";
    CharSequence value = "value";

    assertEquals("name: value\n", mmap.add(name, value).toString());
  }

  @Test
  public void testAddTest8()
      throws Exception {
    MultiMap mmap = newMultiMap();
    CharSequence name = "name";
    ArrayList<CharSequence> values = new ArrayList<CharSequence>();
    values.add("somevalue");

    assertEquals("name: somevalue\n", mmap.add(name, values).toString());
  }

  @Test
  public void testAddTest9()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "";
    ArrayList<CharSequence> values = new ArrayList<CharSequence>();
    values.add("somevalue");

    assertEquals(": somevalue\n", mmap.add(name, values).toString());
  }

  @Test
  public void testAddTest10()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "a";
    ArrayList<CharSequence> values = new ArrayList<CharSequence>();
    values.add("somevalue");

    assertEquals("a: somevalue\n", mmap.add(name, values).toString());
  }

  @Test
  public void testAddTest11()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "";
    String strVal = "";

    assertEquals(": \n", mmap.add(name, strVal).toString());
  }

  @Test
  public void testAddTest12()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "a";
    String strVal = "b";

    assertEquals("a: b\n", mmap.add(name, strVal).toString());
  }

  @Test
  public void testAddTest13()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "aaa";
    String strVal = "";

    assertEquals("aaa: \n", mmap.add(name, strVal).toString());
  }

  @Test
  public void testAddTest14()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "";
    String strVal = "aaa";

    assertEquals(": aaa\n", mmap.add(name, strVal).toString());
  }

  @Test
  public void testAddIterable()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "name";
    List<String> values = new ArrayList<String>();
    values.add("value1");
    values.add("value2");

    MultiMap result = mmap.add(name, values);

    assertEquals(1, result.size());
    assertEquals("name: value1\nname: value2\n", result.toString());
  }

  @Test
  public void testAddMultiMap()
      throws Exception {
    MultiMap mmap = newMultiMap();

    MultiMap mm = newMultiMap();
    mm.add("Header1", "value1");
    mm.add("Header2", "value2");

    MultiMap result = mmap.addAll(mm);

    assertEquals(2, result.size());
    assertEquals("Header1: value1\nHeader2: value2\n", result.toString());
  }

  @Test
  public void testClearTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();

    MultiMap result = mmap.clear();

    assertNotNull(result);
    assertTrue(result.isEmpty());
    assertEquals(0, result.size());
    assertEquals("", result.toString());
  }

  @Test
  public void testContainsTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();
    CharSequence name = String.valueOf(new Object());

    assertFalse(mmap.contains(name));
  }

  @Test
  public void testContainsTest2()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "";

    assertFalse(mmap.contains(name));
  }

  @Test
  public void testContainsTest3()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "0123456789";

    boolean result = mmap.contains(name);

    assertFalse(result);
    mmap.add(name, "");
    result = mmap.contains(name);
    assertTrue(result);
  }

  @Test
  public void testEntriesTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();

    List<Map.Entry<String, String>> result = mmap.entries();

    assertNotNull(result);
    assertEquals(0, result.size());
  }

  @Test
  public void testGetTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();
    CharSequence name = String.valueOf(new Object());

    assertNull(mmap.get(name));
  }

  @Test
  public void testGetTest2()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "1";

    assertNull(mmap.get(name));
  }

  @Test
  public void testGetTest3()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "name";

    String result = mmap.get(name);
    assertNull(result);
    mmap.add(name, "value");
    result = mmap.get(name);
    assertEquals("value", result);
  }

  @Test(expected = NullPointerException.class)
  public void testGetNPE() {
    new CaseInsensitiveHeaders().get(null);
  }

  @Test
  public void testGetAllTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();
    CharSequence name = String.valueOf(new Object());

    List<String> result = mmap.getAll(name);

    assertNotNull(result);
    assertEquals(0, result.size());
  }

  @Test
  public void testGetAllTest2()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "1";

    List<String> result = mmap.getAll(name);

    assertNotNull(result);
    assertEquals(0, result.size());
  }

  @Test
  public void testGetAllTest3()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "name";

    List<String> result = mmap.getAll(name);

    assertNotNull(result);
    assertEquals(0, result.size());
  }

  @Test
  public void testGetAll()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "name";
    mmap.add(name, "value1");
    mmap.add(name, "value2");

    List<String> result = mmap.getAll(name);

    assertNotNull(result);
    assertEquals(2, result.size());
    assertEquals("value1", result.get(0));
  }

  @Test(expected = NullPointerException.class)
  public void testGetAllNPE()
      throws Exception {
    new CaseInsensitiveHeaders().getAll(null);
  }

  @Test
  public void testIsEmptyTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();

    assertTrue(mmap.isEmpty());
  }

  @Test
  public void testIsEmptyTest2()
      throws Exception {
    MultiMap mmap = newMultiMap();
    mmap.add("a", "b");

    assertFalse(mmap.isEmpty());
  }

  @Test
  public void testIteratorTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();

    Iterator<Map.Entry<String, String>> result = mmap.iterator();

    assertNotNull(result);
    assertFalse(result.hasNext());
  }

  @Test
  public void testIteratorTest2()
      throws Exception {
    MultiMap mmap = newMultiMap();
    mmap.add("a", "b");

    Iterator<Map.Entry<String, String>> result = mmap.iterator();

    assertNotNull(result);
    assertTrue(result.hasNext());
  }

  @Test
  public void testNamesTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();

    Set<String> result = mmap.names();

    assertNotNull(result);
    assertEquals(0, result.size());
  }

  @Test
  public void testRemoveTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();
    CharSequence name = String.valueOf(new Object());

    MultiMap result = mmap.remove(name);

    assertNotNull(result);
    assertTrue(result.isEmpty());
    assertEquals(0, result.size());
  }

  @Test(expected = NullPointerException.class)
  public void testRemoveNPE()
      throws Exception {
    new CaseInsensitiveHeaders().remove(null);
  }

  @Test
  public void testRemoveTest2()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "1";

    MultiMap result = mmap.remove(name);

    assertNotNull(result);
    assertTrue(result.isEmpty());
    assertEquals(0, result.size());
  }

  @Test
  public void testRemoveTest3()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "name";

    MultiMap result = mmap.remove(name);

    assertNotNull(result);
    assertTrue(result.isEmpty());
    assertEquals(0, result.size());
  }

  @Test
  public void testRemoveTest4()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "name";
    String value = "value";
    mmap.add(name, value);

    assertTrue(mmap.contains(name));

    MultiMap result = mmap.remove(name);

    assertFalse(result.contains(name));
  }

  @Test
  public void testSetTest1()
      throws Exception {
    MultiMap mmap = newMultiMap();
    HashMap<String, String> headers = new HashMap<String, String>();
    headers.put("", "");

    MultiMap result = mmap.setAll(headers);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals(": \n", result.toString());
  }

  @Test
  public void testSetTest2()
      throws Exception {
    MultiMap mmap = newMultiMap();
    HashMap<String, String> headers = new HashMap<String, String>();
    headers.put("", "");
    headers.put("aaa", "bbb");

    MultiMap result = mmap.setAll(headers);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(2, result.size());
    assertEquals(": \naaa: bbb\n", result.toString());
  }

  @Test
  public void testSetTest3()
      throws Exception {
    MultiMap mmap = newMultiMap();
    HashMap<String, String> headers = new HashMap<String, String>();
    headers.put("aaa", "bbb");

    MultiMap result = mmap.setAll(headers);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals("aaa: bbb\n", result.toString());
  }

  @Test
  public void testSetTest4()
      throws Exception {
    MultiMap mmap = newMultiMap();
    Map<String, String> headers = new HashMap<String, String>();

    MultiMap result = mmap.setAll(headers);

    assertNotNull(result);
    assertTrue(result.isEmpty());
    assertEquals(0, result.size());
    assertEquals("", result.toString());
  }

  @Test
  public void testSetTest5()
      throws Exception {
    MultiMap mmap = newMultiMap();
    MultiMap headers = newMultiMap();

    MultiMap result = mmap.setAll(headers);

    assertNotNull(result);
    assertTrue(result.isEmpty());
    assertEquals(0, result.size());
    assertEquals("", result.toString());
  }

  @Test
  public void testSetTest7()
      throws Exception {
    MultiMap mmap = newMultiMap();
    CharSequence name = "name";
    CharSequence value = "value";

    MultiMap result = mmap.set(name, value);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals("name: value\n", result.toString());
  }

  @Test
  public void testSetTest8()
      throws Exception {
    MultiMap mmap = newMultiMap();
    CharSequence name = "name";
    ArrayList<CharSequence> values = new ArrayList<CharSequence>();
    values.add("somevalue");

    assertEquals("name: somevalue\n", mmap.set(name, values).toString());
  }

  @Test
  public void testSetTest9()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "";
    ArrayList<CharSequence> values = new ArrayList<CharSequence>();
    values.add("somevalue");

    assertEquals(": somevalue\n", mmap.set(name, values).toString());
  }

  @Test
  public void testSetTest10()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "aaa";
    ArrayList<CharSequence> values = new ArrayList<CharSequence>();
    values.add("somevalue");

    assertEquals("aaa: somevalue\n", mmap.set(name, values).toString());
  }

  @Test
  public void testSetTest11()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "";
    String strVal = "";

    MultiMap result = mmap.set(name, strVal);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals(": \n", result.toString());
  }

  @Test
  public void testSetTest12()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "aaa";
    String strVal = "bbb";

    MultiMap result = mmap.set(name, strVal);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals("aaa: bbb\n", result.toString());
  }

  @Test
  public void testSetTest13()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "aaa";
    String strVal = "";

    MultiMap result = mmap.set(name, strVal);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals("aaa: \n", result.toString());
  }

  @Test
  public void testSetTest14()
      throws Exception {
    MultiMap mmap = newMultiMap();
    String name = "";
    String strVal = "bbb";

    MultiMap result = mmap.set(name, strVal);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals(": bbb\n", result.toString());
  }

  @Test(expected = NullPointerException.class)
  public void testSetIterableNPE()
      throws Exception {
    new CaseInsensitiveHeaders().set("name", (Iterable<String>) null);
  }

  @Test
  public void testSetIterableEmpty()
      throws Exception {
    MultiMap mmap = newMultiMap();

    String name = "name";
    List<String> values = new ArrayList<String>();

    MultiMap result = mmap.set(name, values);

    assertNotNull(result);
    assertTrue(result.isEmpty());
    assertEquals(0, result.size());
    assertEquals("", result.toString());
  }

  @Test
  public void testSetIterable()
      throws Exception {
    MultiMap mmap = newMultiMap();

    String name = "name";
    List<String> values = new ArrayList<String>();
    values.add("value1");
    values.add(null);

    MultiMap result = mmap.set(name, values);

    assertNotNull(result);
    assertFalse(result.isEmpty());
    assertEquals(1, result.size());
    assertEquals("name: value1\n", result.toString());
  }

  @Test
  public void testSize()
      throws Exception {
    MultiMap mmap = newMultiMap();

    assertEquals(0, mmap.size());
    mmap.add("header", "value");
    assertEquals(1, mmap.size());
    mmap.add("header2", "value2");
    assertEquals(2, mmap.size());
    mmap.add("header", "value3");
    assertEquals(2, mmap.size());
  }

  @Test
  public void testGetHashColl() {
    MultiMap mm = newMultiMap();

    String name1 = this.sameHash1;
    String name2 = this.sameHash2;
    mm.add(name1, "value1");
    mm.add(name2, "value2");
    assertEquals(2, mm.size());
    assertEquals("value1", mm.get(name1));
    assertEquals("value2", mm.get(name2));

    // same bucket, different hash
    mm = newMultiMap();
    name1 = this.sameBucket1;
    name2 = this.sameBucket2;
    mm.add(name1, "value1");
    mm.add(name2, "value2");
    assertEquals(2, mm.size());
    assertEquals("value1", mm.get(name1));
    assertEquals("value2", mm.get(name2));
  }

  @Test
  public void testGetAllHashColl() {
    MultiMap mm = newMultiMap();
    String name1 = this.sameHash1;
    String name2 = this.sameHash2;
    mm.add(name1, "value1");
    mm.add(name2, "value2");
    assertEquals(2, mm.size());
    assertEquals("[value1]", mm.getAll(name1).toString());
    assertEquals("[value2]", mm.getAll(name2).toString());

    mm = newMultiMap();
    name1 = this.sameBucket1;
    name2 = this.sameBucket2;
    mm.add(name1, "value1");
    mm.add(name2, "value2");
    assertEquals(2, mm.size());
    assertEquals("[value1]", mm.getAll(name1).toString());
    assertEquals("[value2]", mm.getAll(name2).toString());
  }

  @Test
  public void testRemoveHashColl() {
    MultiMap mm = newMultiMap();
    String name1 = this.sameHash1;
    String name2 = this.sameHash2;
    String name3 = "RZ";
    mm.add(name1, "value1");
    mm.add(name2, "value2");
    mm.add(name3, "value3");
    mm.add(name1, "value4");
    mm.add(name2, "value5");
    mm.add(name3, "value6");
    assertEquals(3, mm.size());
    mm.remove(name1);
    mm.remove(name2);
    assertEquals(1, mm.size());

    mm = new CaseInsensitiveHeaders();
    name1 = this.sameBucket1;
    name2 = this.sameBucket2;
    mm.add(name1, "value1");
    mm.add(name2, "value2");
    assertEquals(2, mm.size());
    mm.remove(name1);
    mm.remove(name2);
    assertTrue("not empty", mm.isEmpty());
  }

  // hash function copied from method under test
  private static int hash(String name) {
    int h = 0;
    for (int i = name.length() - 1; i >= 0; i--) {
      char c = name.charAt(i);
      if (c >= 'A' && c <= 'Z') {
        c += 32;
      }
      h = 31 * h + c;
    }

    if (h > 0) {
      return h;
    } else if (h == Integer.MIN_VALUE) {
      return Integer.MAX_VALUE;
    } else {
      return -h;
    }
  }

  private static int index(int hash) {
    return hash % 17;
  }

  // construct a string with hash==MIN_VALUE
  // to get coverage of the if in hash()
  // we will calculate the representation of
  // MAX_VALUE+1 in base31, which wraps around to
  // MIN_VALUE in int representation
  @Test
  public void testHashMININT() {
    MultiMap mm = newMultiMap();
    String name1 = "";
    long value = Integer.MAX_VALUE;
    value++;
    int base = 31;
    long pow = 1;

    while (value > pow * base) {
      pow *= base;
    }

    while (pow != 0) {
      long mul = value / pow;
      name1 = ((char) mul) + name1;
      value -= pow * mul;
      pow /= base;
    }
    name1 = ((char) value) + name1;
    mm.add(name1, "value");
    assertEquals("value", mm.get(name1));
  }

  // we have to sort the string since a map doesn't do sorting
  private String sortByLine(String str) {
    String[] lines = str.split("\n");
    Arrays.sort(lines);
    StringBuilder sb = new StringBuilder();
    for (String s:lines) {
      sb.append(s);
      sb.append("\n");
    }
    return sb.toString();
  }

  @Test
  public void testToString() {
    MultiMap mm = newMultiMap();
    assertEquals("", mm.toString());
    mm.add("Header1", "Value1");
    assertEquals("Header1: Value1\n",
        sortByLine(mm.toString()));
    mm.add("Header2", "Value2");
    assertEquals("Header1: Value1\n"
        + "Header2: Value2\n",
        sortByLine(mm.toString()));
    mm.add("Header1", "Value3");
    assertEquals("Header1: Value1\n"
        + "Header1: Value3\n"
        + "Header2: Value2\n",
        sortByLine(mm.toString()));
    mm.remove("Header1");
    assertEquals("Header2: Value2\n",
        sortByLine(mm.toString()));
    mm.set("Header2", "Value4");
    assertEquals("Header2: Value4\n",
        sortByLine(mm.toString()));
  }

  /*
   * unit tests for public method in MapEntry
   * (isn't actually used in the implementation)
   */

  @Test
  public void testMapEntrySetValue() throws Exception {
    MultiMap mmap = newMultiMap();

    mmap.add("Header", "oldvalue");

    for (Map.Entry<String, String> me:mmap) {
      me.setValue("newvalue");
    }
    assertEquals("newvalue", mmap.get("Header"));
  }

  @Test
  public void testMapEntryToString() throws Exception {
    MultiMap mmap = newMultiMap();

    mmap.add("Header", "value");

    assertEquals("Header: value", mmap.iterator().next().toString());
  }

  @Test(expected = NullPointerException.class)
  public void testMapEntrySetValueNull() throws Exception {
    MultiMap mmap = newMultiMap();

    mmap.add("Header", "oldvalue");

    for (Map.Entry<String, String> me:mmap) {
      me.setValue(null);
    }
  }

  @Test
  public void testContainsValueString() {
    MultiMap mmap = newMultiMap();

    mmap.add("headeR", "vAlue");

    assertTrue(mmap.contains("heaDer", "vAlue", false));
    assertFalse(mmap.contains("heaDer", "Value", false));
  }

  @Test
  public void testContainsValueStringIgnoreCase() {
    MultiMap mmap = newMultiMap();

    mmap.add("headeR", "vAlue");

    assertTrue(mmap.contains("heaDer", "vAlue", true));
    assertTrue(mmap.contains("heaDer", "Value", true));
  }

  @Test
  public void testContainsValueCharSequence() {
    MultiMap mmap = newMultiMap();

    mmap.add("headeR", "vAlue");

    CharSequence name = HttpHeaders.createOptimized("heaDer");
    CharSequence vAlue = HttpHeaders.createOptimized("vAlue");
    CharSequence Value = HttpHeaders.createOptimized("Value");
    assertTrue(mmap.contains(name, vAlue, false));
    assertFalse(mmap.contains(name, Value, false));
  }

  @Test
  public void testContainsValueCharSequenceIgnoreCase() {
    MultiMap mmap = newMultiMap();

    mmap.add("headeR", "vAlue");

    CharSequence name = HttpHeaders.createOptimized("heaDer");
    CharSequence vAlue = HttpHeaders.createOptimized("vAlue");
    CharSequence Value = HttpHeaders.createOptimized("Value");
    assertTrue(mmap.contains(name, vAlue, true));
    assertTrue(mmap.contains(name, Value, true));
  }

  @Test
  public void testGetShouldReturnAddedEntriesInOrder() {
    MultiMap mmap = newMultiMap();
    mmap.add("header", "value1");
    mmap.add("header", "value2");
    mmap.add("header", "value3");
    assertEquals("value1", mmap.get("header"));
    assertEquals(Arrays.asList("value1", "value2", "value3"), mmap.getAll("header"));
  }

  @Test
  public void testGetShouldReturnEntriesFromListInOrder() {
    MultiMap mmap = newMultiMap();
    mmap.add("header", Arrays.<CharSequence>asList("value1", "value2", "value3"));
    assertEquals("value1", mmap.get("header"));
    assertEquals(Arrays.asList("value1", "value2", "value3"), mmap.getAll("header"));
  }
}
