/*
* Lokomo OneCMDB - An Open Source Software for Configuration
* Management of Datacenter Resources
*
* Copyright (C) 2006 Lokomo Systems AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via
* paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33
* Danderyd, Sweden.
*
*/
package org.onecmdb.core.tests.core;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
import java.util.HashMap;
import java.util.Map;
import junit.framework.TestCase;
import org.onecmdb.core.internal.model.ItemId;
/*
* Test the ItemId id generation so it's unique.
*
*/
public class TestItemId extends TestCase {
/**
* Simple encapsualtion for statistics calculation using running sums, that
* is, sample values are forgotten once they are added to det data set.
* <p>For each sample, in the population, just make a call to
* {@link #addSample(double)}. Then, just ask for the statistical value
* interseted in.</p>
*/
private static class DataSet {
private final String name;
private long N = 0; // number of samples
private double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
private double sum = 0.0; // sum
private double sum2 = 0.0; // square sum
/**
* <p>Creates a new data set and gives it a suitable name.</p>
*
* <p>The name is used to diffrentiate data sets from each other when
* serveral data sets are used concurrently, or when nested nested data
* sets ore in use.</p>
* @param name The name for the data set
*/
public DataSet(String name) {
this.name = name;
}
/**
* Adds a sample to this calculation. Internally running sums are
* updated, as well as min and max, etc.
* @param x
*/
public void addSample(double x) {
N++;
sum += x;
sum2 += x * x;
min = min(min, x);
max = max(max, x);
}
/**
* The mean value on the current data set.
* @return
*/
double getMean() {
return getTotal() / getSamples();
}
/**
* Number of samples added
* @return
*/
public long getSamples() {
return N;
}
/**
* The sum of all samples in the current data set.
* @return
*/
public double getTotal() {
return sum;
}
/**
* The sum of all <em>sample squares</em> of the current data set.
* @return
*/
public double getSqrTotal() {
return sum2;
}
/**
* Convienent method to view the statistics n the current data set.
*/
public String toString() {
return "["+name + getSamples() + "] sum="+getTotal() + ";avg=" + getMean() + ";std="+getStdDev() + ";min="+min+";max="+max;
}
/**
* Clears the built up values in this data set, suitable when creating
* a new data set is not needed.
*/
public void reset() {
N = 0;
sum = 0.0;
sum2 = 0.0;
max = Double.MIN_VALUE;
min = Double.MAX_VALUE;
}
/**
* The standard deviation for this data set
* @return
*/
double getStdDev() {
return sqrt(getVariance());
}
/**
* The standard deviation for this data set
* @return
*/
public double getVariance() {
return (N * sum2 - pow(sum, 2)) / (N * (N -1));
}
/**
* The maximum value in this data set
* @return
*/
public double getMaximum() {
return this.max;
}
/**
* The minimum value in this data set
* @return
*/
public double getMinimum() {
return this.min;
}
}
static class Timing {
final long snapshotN;
private final long snapshotTimeout;
private int splits;
/**
* A class used to time certain events and collect statistics. Every
* time a timing takes palace, i.e. a sample, a call to {@link #time}
* should be used.
*
* Each call to <tt>time</tt> may dicatate that a split should be taken.
* If a snap should occur depends on the spit
*
* <p>A snapshot is taken for every snapshotN sample, or if the elapsed
* time since the last snapshot more than the snapshotTimeout, whichever
* occurs first.</p>
*
*
* @param snapshot Max number of samples between snapshots
* @param timeout Maximum time in between snapshots.
*/
Timing(long snapshotN, long snapshotTimeout) {
this.snapshotN = snapshotN;
this.snapshotTimeout = snapshotTimeout;
}
final DataSet total = new DataSet("TOTAL");
final DataSet split = new DataSet("LAST");
/**
* Executes one <em>round</em>, or sample, of work and updates statistics
* regarding elapased time for it to complete. The idea is to time
* the same work several times to build up reliable statistics.
*
* <p>If a snapshot is reached, infomration is printed on stdout.</p>
*
* @param work The work to be messured.
*/
void time(Runnable work) {
long start = System.currentTimeMillis();
work.run();
long delta = System.currentTimeMillis() - start;
total.addSample(delta);
split.addSample(delta);
if ( split.getSamples() == snapshotN || (split.getTotal() >= snapshotTimeout )) {
splits++;
System.out.println(this);
split.reset();
}
}
void stop() {
if (split.getSamples() != 0) {
System.out.println(this);
}
}
public String toString() {
return total + " " + split;
}
public void reset() {
total.reset();
split.reset();
System.out.println(this);
}
public DataSet getTotal() {
return total;
}
}
final int N = 5000;
final int workN = 1000;
public void testReinstantiate() {
Timing stat = new Timing(140000, 2800);
final Runnable w1 = new Runnable() {
public void run() {
for (int i = 0; i < workN; i++) {
ItemId id = new ItemId("fedca9876543210");
String s = id.toString();
ItemId id2 = new ItemId(s);
assertEquals(id, id2);
}
}
};
for (int n = 0; n < N; n++ ) {
stat.time(w1);
}
System.out.println("-- TOTAL: " + N + "x" + workN + " --");
stat.stop();
assertTrue("ID reinstantiaion", stat.getTotal().getMean() / 1000 < 3.0 );
}
public void testCreateNew() {
Timing stat = new Timing(140000, 2800);
final Runnable w2 = new Runnable() {
public void run() {
for (int i = 0; i < workN; i++) {
ItemId id = new ItemId();
String s = id.toString();
ItemId id2 = new ItemId(s);
assertEquals(id, id2);
}
}
};
for (int n = 0; n < N; n++ ) {
stat.time(w2);
}
System.out.println("-- TOTAL: " + N + "x" + workN + " --");
stat.stop();
assertTrue("ID reinstantiaion", stat.getTotal().getMean() / 1000 < 3.0 );
}
public void testStatistics() {
DataSet s = new DataSet("TEST");
s.addSample(5);
s.addSample(6);
s.addSample(3);
s.addSample(4);
double stddev = Math.pow( 5 - 4.5, 2);
stddev += Math.pow( 6 - 4.5, 2);
stddev += Math.pow( 3 - 4.5, 2);
stddev += Math.pow( 4 - 4.5, 2);
stddev = Math.sqrt(stddev / 3);
System.out.println("Calced Std Dev:" + stddev);
System.out.println("Mean: " + s.getMean());
System.out.println("Std Dev: " + s.getStdDev());
System.out.println("Variance: " + s.getVariance());
System.out.println("Total: " + s.getTotal());
System.out.println("Maximum: " + s.getMaximum());
System.out.println("Minimum: " + s.getMinimum());
}
public void testDuplicates() {
final int outsideN = 30;
final int insideN = 10000;
Timing stat = new Timing(5000, 5000);
class TestData {
int n = 0;
int dups = 0;
};
class Stuff {
final long added = System.currentTimeMillis();
final ItemId id;
final int iteration;
Stuff(int n, ItemId id) {
this.iteration = n;
this.id = id;
}
}
final Map<Long, Stuff> ids = new HashMap<Long,Stuff>(); //(int) ((outsideN * insideN) / 0.75));
final TestData testdata = new TestData();
final Runnable work = new Runnable() {
public void run() {
for (int i = 0; i < insideN ; i++) {
testdata.n++;
ItemId id = new ItemId();
Long l = id.asLong();
Stuff dup = ids.put(l, new Stuff((testdata.n * insideN + i), id));
assertEquals(testdata.n, ids.size());
if (dup != null) {
assertFalse( dup.id.getDelegate().equals(id.getDelegate()) );
System.err.println("duplicate entry in map:\n"
+ " Last occurence @ iteration# " + dup.iteration + ";id=" + dup.id.getDelegate() + "\n"
+ " This occurence @ iteration# " + (testdata.n * insideN + i) +";id=" + id.getDelegate());
testdata.dups++;
}
assertTrue("Less than 20 duplicates allowed: ", testdata.dups < 20);
}
}
};
for (int i = 0; i < outsideN; i++) {
stat.time(work);
}
System.out.println("-- TOTAL: " + outsideN + "x" + insideN + " --");
stat.stop();
assertEquals(0, testdata.dups);
}
}