Home | History | Annotate | Download | only in grpc
      1 /*
      2  * Copyright 2014 The gRPC Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package io.grpc;
     18 
     19 import static com.google.common.base.Charsets.US_ASCII;
     20 import static com.google.common.base.Charsets.UTF_8;
     21 import static org.junit.Assert.assertArrayEquals;
     22 import static org.junit.Assert.assertEquals;
     23 import static org.junit.Assert.assertFalse;
     24 import static org.junit.Assert.assertNotEquals;
     25 import static org.junit.Assert.assertNull;
     26 import static org.junit.Assert.assertTrue;
     27 import static org.junit.Assert.fail;
     28 
     29 import com.google.common.collect.Lists;
     30 import io.grpc.Metadata.Key;
     31 import io.grpc.internal.GrpcUtil;
     32 import java.util.Arrays;
     33 import java.util.Iterator;
     34 import java.util.Locale;
     35 import org.junit.Rule;
     36 import org.junit.Test;
     37 import org.junit.rules.ExpectedException;
     38 import org.junit.runner.RunWith;
     39 import org.junit.runners.JUnit4;
     40 
     41 /**
     42  * Tests for {@link Metadata}.
     43  */
     44 @RunWith(JUnit4.class)
     45 public class MetadataTest {
     46 
     47   @Rule public final ExpectedException thrown = ExpectedException.none();
     48 
     49   private static final Metadata.BinaryMarshaller<Fish> FISH_MARSHALLER =
     50       new Metadata.BinaryMarshaller<Fish>() {
     51     @Override
     52     public byte[] toBytes(Fish fish) {
     53       return fish.name.getBytes(UTF_8);
     54     }
     55 
     56     @Override
     57     public Fish parseBytes(byte[] serialized) {
     58       return new Fish(new String(serialized, UTF_8));
     59     }
     60   };
     61 
     62   private static final String LANCE = "lance";
     63   private static final byte[] LANCE_BYTES = LANCE.getBytes(US_ASCII);
     64   private static final Metadata.Key<Fish> KEY = Metadata.Key.of("test-bin", FISH_MARSHALLER);
     65 
     66   @Test
     67   public void noPseudoHeaders() {
     68     thrown.expect(IllegalArgumentException.class);
     69     thrown.expectMessage("Invalid character");
     70 
     71     Metadata.Key.of(":test-bin", FISH_MARSHALLER);
     72   }
     73 
     74   @Test
     75   public void testMutations() {
     76     Fish lance = new Fish(LANCE);
     77     Fish cat = new Fish("cat");
     78     Metadata metadata = new Metadata();
     79 
     80     assertEquals(null, metadata.get(KEY));
     81     metadata.put(KEY, lance);
     82     assertEquals(Arrays.asList(lance), Lists.newArrayList(metadata.getAll(KEY)));
     83     assertEquals(lance, metadata.get(KEY));
     84     metadata.put(KEY, lance);
     85     assertEquals(Arrays.asList(lance, lance), Lists.newArrayList(metadata.getAll(KEY)));
     86     assertTrue(metadata.remove(KEY, lance));
     87     assertEquals(Arrays.asList(lance), Lists.newArrayList(metadata.getAll(KEY)));
     88 
     89     assertFalse(metadata.remove(KEY, cat));
     90     metadata.put(KEY, cat);
     91     assertEquals(cat, metadata.get(KEY));
     92     metadata.put(KEY, lance);
     93     assertEquals(Arrays.asList(lance, cat, lance), Lists.newArrayList(metadata.getAll(KEY)));
     94     assertEquals(lance, metadata.get(KEY));
     95     assertTrue(metadata.remove(KEY, lance));
     96     assertEquals(Arrays.asList(cat, lance), Lists.newArrayList(metadata.getAll(KEY)));
     97     metadata.put(KEY, lance);
     98     assertTrue(metadata.remove(KEY, cat));
     99     assertEquals(Arrays.asList(lance, lance), Lists.newArrayList(metadata.getAll(KEY)));
    100 
    101     assertEquals(Arrays.asList(lance, lance), Lists.newArrayList(metadata.removeAll(KEY)));
    102     assertEquals(null, metadata.getAll(KEY));
    103     assertEquals(null, metadata.get(KEY));
    104   }
    105 
    106   @Test
    107   public void discardAll() {
    108     Fish lance = new Fish(LANCE);
    109     Metadata metadata = new Metadata();
    110 
    111     metadata.put(KEY, lance);
    112     metadata.discardAll(KEY);
    113     assertEquals(null, metadata.getAll(KEY));
    114     assertEquals(null, metadata.get(KEY));
    115   }
    116 
    117   @Test
    118   public void discardAll_empty() {
    119     Metadata metadata = new Metadata();
    120     metadata.discardAll(KEY);
    121     assertEquals(null, metadata.getAll(KEY));
    122     assertEquals(null, metadata.get(KEY));
    123   }
    124 
    125   @Test
    126   public void testGetAllNoRemove() {
    127     Fish lance = new Fish(LANCE);
    128     Metadata metadata = new Metadata();
    129     metadata.put(KEY, lance);
    130     Iterator<Fish> i = metadata.getAll(KEY).iterator();
    131     assertEquals(lance, i.next());
    132 
    133     thrown.expect(UnsupportedOperationException.class);
    134     i.remove();
    135   }
    136 
    137   @Test
    138   public void testWriteParsed() {
    139     Fish lance = new Fish(LANCE);
    140     Metadata metadata = new Metadata();
    141     metadata.put(KEY, lance);
    142     assertEquals(lance, metadata.get(KEY));
    143     Iterator<Fish> fishes = metadata.<Fish>getAll(KEY).iterator();
    144     assertTrue(fishes.hasNext());
    145     assertEquals(fishes.next(), lance);
    146     assertFalse(fishes.hasNext());
    147     byte[][] serialized = metadata.serialize();
    148     assertEquals(2, serialized.length);
    149     assertEquals("test-bin", new String(serialized[0], US_ASCII));
    150     assertArrayEquals(LANCE_BYTES, serialized[1]);
    151     assertEquals(lance, metadata.get(KEY));
    152     assertEquals(serialized[0], metadata.serialize()[0]);
    153     assertEquals(serialized[1], metadata.serialize()[1]);
    154   }
    155 
    156   @Test
    157   public void testWriteRaw() {
    158     Metadata raw = new Metadata(KEY.asciiName(), LANCE_BYTES);
    159     Fish lance = raw.get(KEY);
    160     assertEquals(lance, new Fish(LANCE));
    161     // Reading again should return the same parsed instance
    162     assertEquals(lance, raw.get(KEY));
    163   }
    164 
    165   @Test
    166   public void testSerializeRaw() {
    167     Metadata raw = new Metadata(KEY.asciiName(), LANCE_BYTES);
    168     byte[][] serialized = raw.serialize();
    169     assertArrayEquals(serialized[0], KEY.asciiName());
    170     assertArrayEquals(serialized[1], LANCE_BYTES);
    171   }
    172 
    173   @Test
    174   public void testMergeByteConstructed() {
    175     Metadata raw = new Metadata(KEY.asciiName(), LANCE_BYTES);
    176     Metadata serializable = new Metadata();
    177     serializable.merge(raw);
    178 
    179     byte[][] serialized = serializable.serialize();
    180     assertArrayEquals(serialized[0], KEY.asciiName());
    181     assertArrayEquals(serialized[1], LANCE_BYTES);
    182     assertEquals(new Fish(LANCE), serializable.get(KEY));
    183   }
    184 
    185   @Test
    186   public void headerMergeShouldCopyValues() {
    187     Fish lance = new Fish(LANCE);
    188     Metadata h1 = new Metadata();
    189 
    190     Metadata h2 = new Metadata();
    191     h2.put(KEY, lance);
    192 
    193     h1.merge(h2);
    194 
    195     Iterator<Fish> fishes = h1.<Fish>getAll(KEY).iterator();
    196     assertTrue(fishes.hasNext());
    197     assertEquals(fishes.next(), lance);
    198     assertFalse(fishes.hasNext());
    199   }
    200 
    201   @Test
    202   public void mergeExpands() {
    203     Fish lance = new Fish(LANCE);
    204     Metadata h1 = new Metadata();
    205     h1.put(KEY, lance);
    206 
    207     Metadata h2 = new Metadata();
    208     h2.put(KEY, lance);
    209     h2.put(KEY, lance);
    210     h2.put(KEY, lance);
    211     h2.put(KEY, lance);
    212 
    213     h1.merge(h2);
    214   }
    215 
    216   @Test
    217   public void shortBinaryKeyName() {
    218     thrown.expect(IllegalArgumentException.class);
    219 
    220     Metadata.Key.of("-bin", FISH_MARSHALLER);
    221   }
    222 
    223   @Test
    224   public void invalidSuffixBinaryKeyName() {
    225     thrown.expect(IllegalArgumentException.class);
    226     thrown.expectMessage("Binary header is named");
    227 
    228     Metadata.Key.of("nonbinary", FISH_MARSHALLER);
    229   }
    230 
    231   @Test
    232   public void verifyToString() {
    233     Metadata h = new Metadata();
    234     h.put(KEY, new Fish("binary"));
    235     h.put(Metadata.Key.of("test", Metadata.ASCII_STRING_MARSHALLER), "ascii");
    236     assertEquals("Metadata(test-bin=YmluYXJ5,test=ascii)", h.toString());
    237 
    238     Metadata t = new Metadata();
    239     t.put(Metadata.Key.of("test", Metadata.ASCII_STRING_MARSHALLER), "ascii");
    240     assertEquals("Metadata(test=ascii)", t.toString());
    241 
    242     t = new Metadata("test".getBytes(US_ASCII), "ascii".getBytes(US_ASCII),
    243         "test-bin".getBytes(US_ASCII), "binary".getBytes(US_ASCII));
    244     assertEquals("Metadata(test=ascii,test-bin=YmluYXJ5)", t.toString());
    245   }
    246 
    247   @Test
    248   public void verifyToString_usingBinary() {
    249     Metadata h = new Metadata();
    250     h.put(KEY, new Fish("binary"));
    251     h.put(Metadata.Key.of("test", Metadata.ASCII_STRING_MARSHALLER), "ascii");
    252     assertEquals("Metadata(test-bin=YmluYXJ5,test=ascii)", h.toString());
    253 
    254     Metadata t = new Metadata();
    255     t.put(Metadata.Key.of("test", Metadata.ASCII_STRING_MARSHALLER), "ascii");
    256     assertEquals("Metadata(test=ascii)", t.toString());
    257   }
    258 
    259   @Test
    260   public void testKeyCaseHandling() {
    261     Locale originalLocale = Locale.getDefault();
    262     Locale.setDefault(new Locale("tr", "TR"));
    263     try {
    264       // In Turkish, both I and i (which are in ASCII) change into non-ASCII characters when their
    265       // case is changed as  and , respectively.
    266       assertEquals("", "i".toUpperCase());
    267       assertEquals("", "I".toLowerCase());
    268 
    269       Metadata.Key<String> keyTitleCase
    270           = Metadata.Key.of("If-Modified-Since", Metadata.ASCII_STRING_MARSHALLER);
    271       Metadata.Key<String> keyLowerCase
    272           = Metadata.Key.of("if-modified-since", Metadata.ASCII_STRING_MARSHALLER);
    273       Metadata.Key<String> keyUpperCase
    274           = Metadata.Key.of("IF-MODIFIED-SINCE", Metadata.ASCII_STRING_MARSHALLER);
    275 
    276       Metadata metadata = new Metadata();
    277       metadata.put(keyTitleCase, "plain string");
    278       assertEquals("plain string", metadata.get(keyTitleCase));
    279       assertEquals("plain string", metadata.get(keyLowerCase));
    280       assertEquals("plain string", metadata.get(keyUpperCase));
    281 
    282       byte[][] bytes = metadata.serialize();
    283       assertEquals(2, bytes.length);
    284       assertArrayEquals("if-modified-since".getBytes(US_ASCII), bytes[0]);
    285       assertArrayEquals("plain string".getBytes(US_ASCII), bytes[1]);
    286     } finally {
    287       Locale.setDefault(originalLocale);
    288     }
    289   }
    290 
    291   @Test
    292   public void removeIgnoresMissingValue() {
    293     Metadata m = new Metadata();
    294     // Any key will work.
    295     Key<String> key = GrpcUtil.USER_AGENT_KEY;
    296 
    297     boolean success = m.remove(key, "agent");
    298     assertFalse(success);
    299   }
    300 
    301   @Test
    302   public void removeAllIgnoresMissingValue() {
    303     Metadata m = new Metadata();
    304     // Any key will work.
    305     Key<String> key = GrpcUtil.USER_AGENT_KEY;
    306 
    307     Iterable<String> removed = m.removeAll(key);
    308     assertNull(removed);
    309   }
    310 
    311   @Test
    312   public void keyEqualsHashNameWorks() {
    313     Key<?> k1 = Key.of("case", Metadata.ASCII_STRING_MARSHALLER);
    314 
    315     Key<?> k2 = Key.of("CASE", Metadata.ASCII_STRING_MARSHALLER);
    316     assertEquals(k1, k1);
    317     assertNotEquals(k1, null);
    318     assertNotEquals(k1, new Object(){});
    319     assertEquals(k1, k2);
    320 
    321     assertEquals(k1.hashCode(), k2.hashCode());
    322     // Check that the casing is preserved.
    323     assertEquals("CASE", k2.originalName());
    324     assertEquals("case", k2.name());
    325   }
    326 
    327   @Test
    328   public void invalidKeyName() {
    329     try {
    330       Key.of("io.grpc/key1", Metadata.ASCII_STRING_MARSHALLER);
    331       fail("Should have thrown");
    332     } catch (IllegalArgumentException e) {
    333       assertEquals("Invalid character '/' in key name 'io.grpc/key1'", e.getMessage());
    334     }
    335   }
    336 
    337   private static class Fish {
    338     private String name;
    339 
    340     private Fish(String name) {
    341       this.name = name;
    342     }
    343 
    344     @Override
    345     public boolean equals(Object o) {
    346       if (this == o) {
    347         return true;
    348       }
    349       if (o == null || getClass() != o.getClass()) {
    350         return false;
    351       }
    352       Fish fish = (Fish) o;
    353       if (name != null ? !name.equals(fish.name) : fish.name != null) {
    354         return false;
    355       }
    356       return true;
    357     }
    358 
    359     @Override
    360     public int hashCode() {
    361       return name.hashCode();
    362     }
    363 
    364     @Override
    365     public String toString() {
    366       return "Fish(" + name + ")";
    367     }
    368   }
    369 }
    370