/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.instrumentation.api.internal.cache;

import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

class CacheTest {

  @Nested
  @SuppressWarnings("ClassCanBeStatic")
  class StrongKeys {
    @Test
    void bounded() {
      Cache<String, String> cache = Cache.bounded(1);

      assertThat(cache.computeIfAbsent("bear", unused -> "roar")).isEqualTo("roar");
      cache.remove("bear");

      MapBackedCache<?, ?> mapBackedCache = ((MapBackedCache<?, ?>) cache);
      assertThat(cache.computeIfAbsent("cat", unused -> "meow")).isEqualTo("meow");
      assertThat(mapBackedCache.size()).isEqualTo(1);

      assertThat(cache.computeIfAbsent("cat", unused -> "bark")).isEqualTo("meow");
      assertThat(mapBackedCache.size()).isEqualTo(1);

      cache.put("dog", "bark");
      assertThat(cache.get("dog")).isEqualTo("bark");
      assertThat(mapBackedCache.size()).isEqualTo(1);
      assertThat(cache.computeIfAbsent("cat", unused -> "purr")).isEqualTo("purr");
    }
  }

  @Nested
  @SuppressWarnings("ClassCanBeStatic")
  class WeakKeys {
    @SuppressWarnings("StringOperationCanBeSimplified")
    @Test
    void unbounded() {
      Cache<String, String> cache = Cache.weak();

      assertThat(cache.computeIfAbsent("bear", unused -> "roar")).isEqualTo("roar");
      cache.remove("bear");

      WeakLockFreeCache<?, ?> weakLockFreeCache = ((WeakLockFreeCache<?, ?>) cache);
      String cat = new String("cat");
      String dog = new String("dog");
      assertThat(cache.computeIfAbsent(cat, unused -> "meow")).isEqualTo("meow");
      assertThat(weakLockFreeCache.size()).isEqualTo(1);

      assertThat(cache.computeIfAbsent(cat, unused -> "bark")).isEqualTo("meow");
      assertThat(weakLockFreeCache.size()).isEqualTo(1);

      cache.put(dog, "bark");
      assertThat(cache.get(dog)).isEqualTo("bark");
      assertThat(cache.get(cat)).isEqualTo("meow");
      assertThat(cache.get(new String("dog"))).isNull();
      assertThat(weakLockFreeCache.size()).isEqualTo(2);
      assertThat(cache.computeIfAbsent(cat, unused -> "meow")).isEqualTo("meow");

      cat = null;
      System.gc();
      // Wait for GC to be reflected.
      await().untilAsserted(() -> assertThat(weakLockFreeCache.size()).isEqualTo(1));
      assertThat(cache.computeIfAbsent(dog, unused -> "bark")).isEqualTo("bark");
      dog = null;
      System.gc();
      // Wait for GC to be reflected.
      await().untilAsserted(() -> assertThat(weakLockFreeCache.size()).isEqualTo(0));
    }
  }
}
