public String registerCoder(Coder<?> coder) throws IOException {
    String existing = coderIds.get(coder);
    if (existing != null) {
      return existing;
    }
    // Unlike StructuredCoder, custom coders may not have proper implementation of hashCode() and
    // equals(), this lead to unnecessary duplications. In order to avoid this we examine already
    // registered coders and see if we can find a matching proto, and consider them same coder.
    RunnerApi.Coder coderProto = CoderTranslation.toProto(coder, this);
    if (coderProtoToId.containsKey(coderProto)) {
      return coderProtoToId.get(coderProto);
    }
    String baseName = NameUtils.approximateSimpleName(coder);
    String name = uniqify(baseName, coderIds.values());
    coderIds.put(coder, name);
    coderProtoToId.put(coderProto, name);
    componentsBuilder.putCoders(name, coderProto);
    return name;
  }
@Test
  public void registerCoder() throws IOException {
    Coder<?> coder =
        KvCoder.of(StringUtf8Coder.of(), IterableCoder.of(SetCoder.of(ByteArrayCoder.of())));
    String id = components.registerCoder(coder);
    assertThat(components.registerCoder(coder), equalTo(id));
    assertThat(id, not(isEmptyOrNullString()));
    Coder<?> equalCoder =
        KvCoder.of(StringUtf8Coder.of(), IterableCoder.of(SetCoder.of(ByteArrayCoder.of())));
    assertThat(components.registerCoder(equalCoder), equalTo(id));
    Coder<?> otherCoder = VarLongCoder.of();
    assertThat(components.registerCoder(otherCoder), not(equalTo(id)));

    components.toComponents().getCodersOrThrow(id);
    components.toComponents().getCodersOrThrow(components.registerCoder(otherCoder));
  }