TimeContext.java :  » Development » Javolution » javolution » testing » Java Open Source

Java Open Source » Development » Javolution 
Javolution » javolution » testing » TimeContext.java
/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2007 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution.testing;

import j2me.lang.CharSequence;
import javolution.context.Context;
import javolution.context.LogContext;
import javolution.context.ObjectFactory;
import javolution.lang.Configurable;
import javolution.lang.MathLib;
import javolution.text.TextBuilder;

/**
 * <p> This class represents a {@link TestContext test context} specialized 
 *     for measuring execution time.</p>
 * 
 * <p> {@link TimeContext} implementations may perform assertions based upon the
 *     execution time. For example:[code] 
 *     class MyTestCase extends TestCase() {
 *          ...
 *          public void validate() {
 *              long ns = TimeContext.getAverageTime("ns");
 *              TimeContext.assertTrue(ns < 100); // Error if execution time is more than 100 ns. 
 *              ... 
 *          }
 *     }[/code]</p>
 * 
 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
 * @version 5.2, August 5, 2007
 */
public abstract class TimeContext extends TestContext {

  /**
   * Holds an implementation which does not perform any logging but raises an
   * {@link AssertionException} when an assertion fails, including any timing
   * assertion.
   */
  public static final Class/*<TimeContext>*/REGRESSION = Regression.CLASS;

  /**
   * Holds the minimum duration of each test case execution (default 2000 ms).
   * The larger the number the more accurate is the average time result; but
   * the longer it takes to run the test.
   */
  public static final Configurable/*<Integer>*/
     TEST_DURATION_MS = new Configurable(new Integer(1000));

  /**
   * Holds the time context default implementation (by default logs 
   * average and minimum execution time to <code>System.out</code>).
   */
  public static final Configurable/*<Class<? extends TimeContext>>*/
     DEFAULT = new Configurable(Default.CLASS);

  /**
   * Holds the minimum execution time in picoseconds.
   */
  private long _minimumPs;

  /**
   * Holds the average execution time in picoseconds.
   */
  private long _averagePs;

  /**
   * Holds the maximum execution time in picoseconds.
   */
  private long _maximumPs;

  /**
   * Enters the {@link #DEFAULT} time context.
   * 
   * @return the time context being entered.
   */
  public static TimeContext enter() {
    return (TimeContext) Context.enter((Class) DEFAULT.get());
  }

  /**
   * Exits the current time context.
   * 
   * @return the time context being exited.
   * @throws ClassCastException if the context is not a stack context.
   */
  public static/*TimeContext*/Context exit() {
    return (TimeContext) Context.exit();
  }

  /**
   * Returns the minimum execution time of the latest execution performed or
   * <code>-1</code> if the current context is not a time context.
   * 
   * @param unit one of <code>"s", "ms", "us", "ns", "ps"</code>
   * @return the minimum execution time stated in the specified unit.
   */
  public static long getMinimumTime(String unit) {
    LogContext ctx = (LogContext) LogContext.getCurrent();
    if (ctx instanceof TimeContext) {
      return TimeContext.picosecondTo(unit, ((TimeContext) ctx)
          .getMinimumTimeInPicoSeconds());
    } else {
      return -1;
    }
  }

  /**
   * Returns the average execution time of the latest execution performed or
   * <code>-1</code> if the current context is not a time context.
   * 
   * @param unit one of <code>"s", "ms", "us", "ns", "ps"</code>
   * @return the average execution time stated in the specified unit.
   */
  public static long getAverageTime(String unit) {
    LogContext ctx = (LogContext) LogContext.getCurrent();
    if (ctx instanceof TimeContext) {
      return TimeContext.picosecondTo(unit, ((TimeContext) ctx)
          .getAverageTimeInPicoSeconds());
    } else {
      return -1;
    }
  }

  /**
   * Returns the maximum execution time of the latest execution performed or
   * <code>-1</code> if the current context is not a time context.
   * 
   * @param unit one of <code>"s", "ms", "us", "ns", "ps"</code>
   * @return the maximum execution time stated in the specified unit.
   */
  public static long getMaximumTime(String unit) {
    LogContext ctx = (LogContext) LogContext.getCurrent();
    if (ctx instanceof TimeContext) {
      return TimeContext.picosecondTo(unit, ((TimeContext) ctx)
          .getMaximumTimeInPicoSeconds());
    } else {
      return -1;
    }
  }

  private static long picosecondTo(String unit, long picoseconds) {
    if (unit.equals("ps"))
      return picoseconds;
    if (unit.equals("ns"))
      return picoseconds / 1000;
    if (unit.equals("us"))
      return picoseconds / 1000000;
    if (unit.equals("ms"))
      return picoseconds / 1000000000;
    if (unit.equals("s"))
      return picoseconds / 1000000000000L;
    throw new IllegalArgumentException("Unit " + unit + " not recognized");
  }

  /**
   * Returns the minimum execution time of the latest execution stated in
   * pico-seconds.
   * 
   * @return the time in pico-seconds.
   */
  public long getMinimumTimeInPicoSeconds() {
    return _minimumPs;
  }

  /**
   * Returns the average execution time of the latest execution stated in
   * pico-seconds.
   * 
   * @return the time in pico-seconds.
   */
  public long getAverageTimeInPicoSeconds() {
    return _averagePs;
  }

  /**
   * Returns the maximmum execution time of the latest execution stated in
   * pico-seconds.
   * 
   * @return the time in pico-seconds.
   */
  public long getMaximumTimeInPicoSeconds() {
    return _maximumPs;
  }

  // Overrides.
  public void doTest(TestCase testCase) {
    _testCase = testCase;
    System.gc();
    try {
      Thread.sleep(200); // For GC to run.
    } catch (InterruptedException e) {
    }

    _minimumPs = Long.MAX_VALUE;
    _maximumPs = 0;
    _averagePs = 0;
    long totalCount = 0;
    long totalDuration = 0;
    long maximumDurationPs 
       = ((Integer) TEST_DURATION_MS.get()).intValue() * 1000000000L;
    do {
      testCase.prepare(); // Prepare
      try {
        long start = TimeContext.nanoTime();
        testCase.execute(); // Execute.
        long duration = (TimeContext.nanoTime() - start) * 1000; // Picoseconds
        int count = testCase.count();
        totalCount += count;
        totalDuration += duration;
        long singleExecutionDuration = duration / count;
        if (singleExecutionDuration < _minimumPs) {
          _minimumPs = singleExecutionDuration;
        }
        if (singleExecutionDuration > _maximumPs) {
          _maximumPs = singleExecutionDuration;
        }
        if (totalDuration >= maximumDurationPs) {
          _averagePs = totalDuration / totalCount;
          testCase.validate(); // Validate only at last iteration.
          break;
        }
      } finally {
        testCase.cleanup(); // Cleanup.
      }
    } while (true);
  }
  private TestCase _testCase;

  // Override.
  public boolean doAssertEquals(String message, Object expected, Object actual) {
    if (((expected == null) && (actual != null))
        || ((expected != null) && (!expected.equals(actual)))) {
      LogContext.error(_testCase.toString());
      throw new AssertionException(message, expected, actual);
    }
    return true;
  }

  // Holds the default implementation.
  private static final class Default extends TimeContext {

    private static final Class CLASS = new Default().getClass();

    // Overrides.
    public void doTest(TestCase testCase) {
      super.doTest(testCase);
      TextBuilder tb = TextBuilder.newInstance();
      tb.append("[test] ");
        tb.append(testCase.toString());
      tb.append(": ");
      Default.appendTime(this.getAverageTimeInPicoSeconds(), tb);
      tb.append(" (minimum ");
      Default.appendTime(this.getMinimumTimeInPicoSeconds(), tb);
      tb.append(")");
            tb.println();
      TextBuilder.recycle(tb);
    }

    private static TextBuilder appendTime(long picoseconds, TextBuilder tb) {
      long divisor;
      String unit;
      if (picoseconds > 1000 * 1000 * 1000 * 1000L) { // 1 s
        unit = " s";
        divisor = 1000 * 1000 * 1000 * 1000L;
      } else if (picoseconds > 1000 * 1000 * 1000L) {
        unit = " ms";
        divisor = 1000 * 1000 * 1000L;
      } else if (picoseconds > 1000 * 1000L) {
        unit = " us";
        divisor = 1000 * 1000L;
      } else if (picoseconds > 1000L) {
        unit = " ns";
        divisor = 1000L;
      } else {
        unit = " ps";
        divisor = 1L;
      }
      long value = picoseconds / divisor;
      tb.append(value);
      int fracDigits = 3 - MathLib.digitLength(value); // 3 digits
                                // precision
      if (fracDigits > 0)
        tb.append(".");
      for (int i = 0, j = 10; i < fracDigits; i++, j *= 10) {
        tb.append((picoseconds * j / divisor) % 10);
      }
      return tb.append(unit);
    }

        public boolean isInfoLogged() {
            return true;
        }

        public void logInfo(CharSequence message) {
            System.out.print("[info] ");
            System.out.println(message);
        }

        public boolean isWarningLogged() {
            return true;
        }

        public void logWarning(CharSequence message) {
            System.out.print("[warning] ");
            System.out.println(message);
        }

        public boolean isErrorLogged() {
            return true;
        }

        public void logError(Throwable error, CharSequence message) {
            System.out.print("[error] ");
            if (error != null) {
                System.out.print(error.getClass().getName());
                System.out.print(" - ");
            }
            String description = (message != null) ? message.toString()
                    : (error != null) ? error.getMessage() : "";
            System.out.println(description);
            if (error != null) {
                error.printStackTrace();
            }
        }

  }

  private static long nanoTime() {
    /*@JVM-1.5+@
      if (true) return System.nanoTime(); 
        /**/
      return System.currentTimeMillis() * 1000000;
  }

  // TestContext implementation with no output (just validation).
  private static final class Regression extends TimeContext {

    private static final Class CLASS = new Regression().getClass();

    public boolean isErrorLogged() {
      return false;
    }

    public boolean isInfoLogged() {
      return false;
    }

    public boolean isWarningLogged() {
      return false;
    }

    public void logError(Throwable error, CharSequence message) {
    }

    public void logInfo(CharSequence message) {
    }

    public void logWarning(CharSequence message) {
    }
  }

  // Allows instances of private classes to be factory produced.
  static {
    ObjectFactory.setInstance(new ObjectFactory() {
      protected Object create() {
        return new Default();
      }
    }, Default.CLASS);
    ObjectFactory.setInstance(new ObjectFactory() {
      protected Object create() {
        return new Regression();
      }
    }, Regression.CLASS);
  }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.