All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jio.test.pbt.Property Maven / Gradle / Ivy

Go to download

JIO test library based on Property Based Testing and Java Flight Recording Debuggers

There is a newer version: 3.0.0-RC2
Show newest version
package jio.test.pbt;

import fun.gen.Gen;
import jio.BiLambda;
import jio.IO;
import jsonvalues.JsObj;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Map;
import java.util.Random;
import java.util.SplittableRandom;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.random.RandomGenerator;
import java.util.stream.Collectors;

import static java.util.Objects.requireNonNull;

/**
 * Represents a property of a piece of code or program that should always be held and never fails.
 *
 * @param  the type of the data generated to feed the property tests
 */
public non-sealed class Property extends Testable {

  private static final RandomGenerator seedGen = new SplittableRandom();
  final String name;
  final int times;
  final Gen gen;
  final BiLambda property;

  final String description;

  private final boolean collect;
  private final Path path;
  private final Map> classifiers;

  Property(String name, Gen gen, BiLambda property, String description,
           int times, Path path, boolean collect, Map> classifiers) {
    this.name = name;
    this.gen = gen;
    this.property = property;
    this.collect = collect;
    this.path = path;
    this.classifiers = classifiers;
    this.description = description;
    this.times = times;
  }


  private String getTags(GenValue value) {
      if (classifiers == null) {
          return "";
      }
    return classifiers.keySet()
        .stream()
        .filter(key -> classifiers.get(key).test(value))
        .collect(Collectors.joining(","));
  }


  void dump(Report report) {
    synchronized (Property.class) {
      try {
        Files.writeString(path, report + "\n");
      } catch (IOException e) {
        throw new UncheckedIOException(e);
      }
    }
  }


  /**
   * Returns a new testable instance that represents the property and will be executed in parallel
   * for the specified number of times, using multiple threads from the common ForkJoinPool.
   *
   * @param n the number of parallel executions for the property
   * @return a new testable instance with parallel execution
   */
  public Testable repeatPar(final int n) {
    return new ParProperty<>(n, this);
  }

  /**
   * Returns a new testable instance that represents the property and will be executed sequentially
   * for the specified number of times.
   *
   * @param n the number of sequential executions for the property
   * @return a new testable instance with sequential execution
   */
  public Testable repeatSeq(final int n) {
    return new SeqProperty<>(n, this);
  }

  @Override
  IO createTask(final JsObj conf) {
    Supplier task = () -> {
      Report report = new Report(name, description);
      long seed = seedGen.nextLong();
      Supplier rg = gen.apply(new Random(seed));
      report.setStartTime(Instant.now());
      for (int i = 1; i <= times; i++) {
        report.incTest();
        var tic = Instant.now();
        var generated = rg.get();
        String tags = getTags(generated);
          if (classifiers != null) {
              report.classify(tags);
          }
          if (collect) {
              report.collect(generated == null ? "null" : generated.toString());
          }
        var context = new Context(tic, seed, i, generated, tags);
        property.apply(conf, generated)
            .onResult(tr -> {
                  report.tac(tic);
                    if (tr instanceof TestFailure tf) {
                        report.addFailure(new FailureContext(context, tf));
                    }
                },
                exc -> {
                  report.tac(tic);
                  if (requireNonNull(exc) instanceof TestFailure tf) {
                    report.addFailure(new FailureContext(context, tf));
                  } else {
                    report.addException(new ExceptionContext(context, exc));
                  }
                });
      }
      report.setEndTime(Instant.now());
      return report;
    };

    IO io = IO.managedLazy(task)
        .peekSuccess(Report::summarize);

    return path == null ? io : io.peekSuccess(this::dump);

  }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy