package org.openbravo.test.system;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.RandomStringUtils;
import org.junit.Test;
import org.openbravo.utils.CryptoUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ConcurrentCrypto {
  private static final int THREAD_NUM = 4;
  private static final int LOOPS = 10;

  private static Logger log = LoggerFactory.getLogger(ConcurrentCrypto.class);

  @Test
  public void concurrency() throws InterruptedException, ExecutionException {
    ExecutorService executor = Executors.newFixedThreadPool(THREAD_NUM);

    List<Callable<List<String>>> tasks = new ArrayList<>();

    for (int i = 0; i < THREAD_NUM; i++) {
      tasks.add(new Callable<List<String>>() {
        @Override
        public List<String> call() throws Exception {
          List<String> msg = new ArrayList<>(LOOPS);
          for (int j = 0; j < LOOPS; j++) {
            try {
              String raw = RandomStringUtils.randomAlphanumeric(j);
              String encrypted = CryptoUtility.encrypt(raw);
              String decrypted = CryptoUtility.decrypt(encrypted);
              if (!decrypted.equals(raw)) {
                msg.add("Failed to decrypt");
              }
            } catch (Exception e) {
              msg.add(e.getMessage());
            }
          }
          return msg;
        }
      });
    }

    List<Future<List<String>>> execs = executor.invokeAll(tasks, 5, TimeUnit.MINUTES);
    double totalErrors = 0;
    for (Future<List<String>> exec : execs) {
      for (String msg : exec.get()) {
        log.error(msg);
        totalErrors += 1;
      }
    }
    assertThat("Error ratio", totalErrors / (THREAD_NUM * LOOPS), is(Double.valueOf(0)));
  }
}
