blob: 20040d2b3e4a771ea0413d55108f8ce71bec423e [file] [log] [blame]
Pierre De Ropfaca2892016-01-31 23:27:05 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.dm.lambda.itest;
20
21import java.io.PrintStream;
22
23import org.junit.Assert;
24
25/**
26 * Helper class to make sure that steps in a test happen in the correct order. Instantiate
27 * this class and subsequently invoke <code>step(nr)</code> with steps starting at 1. You
28 * can also have threads wait until you arrive at a certain step.
29 *
30 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
31 */
32public class Ensure {
33 private final boolean DEBUG;
34 private static long INSTANCE = 0;
35 private static final int RESOLUTION = 100;
36 private static PrintStream STREAM = System.out;
37 int step = 0;
38 private Throwable m_throwable;
39
40 public Ensure() {
41 this(true);
42 }
43
44 public Ensure(boolean debug) {
45 DEBUG = debug;
46 if (DEBUG) {
47 INSTANCE++;
48 }
49 }
50
51 public void setStream(PrintStream output) {
52 STREAM = output;
53 }
54
55 /**
56 * Mark this point as step <code>nr</code>.
57 *
58 * @param nr the step we are in
59 */
60 public synchronized void step(int nr) {
61 step++;
62 Assert.assertEquals(nr, step);
63 if (DEBUG) {
64 String info = getLineInfo(3);
65 STREAM.println("[Ensure " + INSTANCE + "] step " + step + " [" + currentThread() + "] " + info);
66 }
67 notifyAll();
68 }
69
70 private String getLineInfo(int depth) {
71 StackTraceElement[] trace = Thread.currentThread().getStackTrace();
72 String info = trace[depth].getClassName() + "." + trace[depth].getMethodName() + ":" + trace[depth].getLineNumber();
73 return info;
74 }
75
76 /**
77 * Mark this point as the next step.
78 */
79 public synchronized void step() {
80 step++;
81 if (DEBUG) {
82 String info = getLineInfo(3);
83 STREAM.println("[Ensure " + INSTANCE + "] next step " + step + " [" + currentThread() + "] " + info);
84 }
85 notifyAll();
86 }
87
88 /**
89 * Wait until we arrive at least at step <code>nr</code> in the process, or fail if that
90 * takes more than <code>timeout</code> milliseconds. If you invoke wait on a thread,
91 * you are effectively assuming some other thread will invoke the <code>step(nr)</code>
92 * method.
93 *
94 * @param nr the step to wait for
95 * @param timeout the number of milliseconds to wait
96 */
97 public synchronized void waitForStep(int nr, int timeout) {
98 final int initialTimeout = timeout;
99 if (DEBUG) {
100 String info = getLineInfo(3);
101 STREAM.println("[Ensure " + INSTANCE + "] waiting for step " + nr + " [" + currentThread() + "] " + info);
102 }
103 while (step < nr && timeout > 0) {
104 try {
105 wait(RESOLUTION);
106 timeout -= RESOLUTION;
107 }
108 catch (InterruptedException e) {}
109 }
110 if (step < nr) {
111 throw new IllegalStateException("Timed out waiting for " + initialTimeout + " ms for step " + nr + ", we are still at step " + step);
112 }
113 if (DEBUG) {
114 String info = getLineInfo(3);
115 STREAM.println("[Ensure " + INSTANCE + "] arrived at step " + nr + " [" + currentThread() + "] " + info);
116 }
117 }
118
119 private String currentThread() {
120 Thread thread = Thread.currentThread();
121 return thread.getId() + " " + thread.getName();
122 }
123
124 public static Runnable createRunnableStep(final Ensure ensure, final int nr) {
125 return new Runnable() { public void run() { ensure.step(nr); }};
126 }
127
128 public synchronized void steps(Steps steps) {
129 steps.next(this);
130 }
131
132 /**
133 * Helper class for naming a list of step numbers. If used with the steps(Steps) method
134 * you can define at which steps in time this point should be passed. That means you can
135 * check methods that will get invoked multiple times during a test.
136 */
137 public static class Steps {
138 private final int[] m_steps;
139 private int m_stepIndex;
140
141 /**
142 * Create a list of steps and initialize the step counter to zero.
143 */
144 public Steps(int... steps) {
145 m_steps = steps;
146 m_stepIndex = 0;
147 }
148
149 /**
150 * Ensure we're at the right step. Will throw an index out of bounds exception if we enter this step more often than defined.
151 */
152 public void next(Ensure ensure) {
153 ensure.step(m_steps[m_stepIndex++]);
154 }
155 }
156
157 /**
158 * Saves a thrown exception that occurred in a different thread. You can only save one exception
159 * at a time this way.
160 */
161 public synchronized void throwable(Throwable throwable) {
162 m_throwable = throwable;
163 }
164
165 /**
166 * Throws a <code>Throwable</code> if one occurred in a different thread and that thread saved it
167 * using the <code>throwable()</code> method.
168 */
169 public synchronized void ensure() throws Throwable {
170 if (m_throwable != null) {
171 throw m_throwable;
172 }
173 }
174}