/*******************************************************************************
* Copyright (c) 2007, 2016 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.tests;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.IMemoryMap;
import org.eclipse.tcf.services.IPathMap;
/**
* TCF Test Suite implements stress testing of communication channels and capabilities of remote peer.
* It is intended to be used before starting a debug session for a first time to make sure the selected
* target is stable and reliable.
*/
public class TCFTestSuite {
final static int NUM_CHANNELS = 4;
private final TestListener listener;
private final IChannel[] channels;
private final Map<IChannel,RunControl> run_controls = new HashMap<IChannel, RunControl>();
private final LinkedList<Runnable> pending_tests = new LinkedList<Runnable>();
private final Collection<Throwable> errors = new ArrayList<Throwable>();
private final Map<ITCFTest,IChannel> active_tests = new HashMap<ITCFTest,IChannel>();
private final static HashMap<String,String> cancel_test_ids = new HashMap<String,String>();
private int count_total;
private int count_done;
boolean cancel;
boolean canceled;
boolean target_lock;
public interface TestListener {
public void progress(String label, int done, int total);
public void done(Collection<Throwable> errors);
}
public TCFTestSuite(final IPeer peer, final TestListener listener, final List<IPathMap.PathMapRule> path_map,
final Map<String,ArrayList<IMemoryMap.MemoryRegion>> mem_map) throws IOException {
this.listener = listener;
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Echo Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestEcho(TCFTestSuite.this, channel), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Echo FP Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestEchoFP(TCFTestSuite.this, channel), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Echo INT Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestEchoINT(TCFTestSuite.this, channel), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Echo ERR Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestEchoERR(TCFTestSuite.this, channel), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Path Map Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestPathMap(TCFTestSuite.this, channel, path_map), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Debugger Attach/Terminate Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestAttachTerminate(TCFTestSuite.this, run_controls.get(channel), channel), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Expressions Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestExpressions(TCFTestSuite.this, run_controls.get(channel), channel, mem_map), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Streams Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestStreams(TCFTestSuite.this, channel), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Sys monitor Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestSysMonitor(TCFTestSuite.this, channel), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Terminals Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestTerminals(TCFTestSuite.this, channel), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
int i = 0;
listener.progress("Running Run Control Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestRCBP1(TCFTestSuite.this, run_controls.get(channel),
channel, i++, path_map, mem_map), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
int i = 0;
listener.progress("Running File System Test...", count_done++, count_total);
for (IChannel channel : channels) {
active_tests.put(new TestFileSystem(TCFTestSuite.this, channel, i++), channel);
}
}
});
pending_tests.add(new Runnable() {
public void run() {
listener.progress("Running Interability Test...", count_done++, count_total);
Random rnd = new Random();
for (int i = 0; i < channels.length; i++) {
IChannel channel = channels[i];
ITCFTest test = null;
switch (rnd.nextInt(11)) {
case 0: test = new TestEcho(TCFTestSuite.this, channel); break;
case 1: test = new TestEchoERR(TCFTestSuite.this, channel); break;
case 2: test = new TestEchoFP(TCFTestSuite.this, channel); break;
case 3: test = new TestAttachTerminate(TCFTestSuite.this, run_controls.get(channel), channel); break;
case 4: test = new TestExpressions(TCFTestSuite.this, run_controls.get(channel), channel, mem_map); break;
case 5: test = new TestRCBP1(TCFTestSuite.this, run_controls.get(channel), channel, i, path_map, mem_map); break;
case 6: test = new TestFileSystem(TCFTestSuite.this, channel, i); break;
case 7: test = new TestPathMap(TCFTestSuite.this, channel, path_map); break;
case 8: test = new TestStreams(TCFTestSuite.this, channel); break;
case 9: test = new TestSysMonitor(TCFTestSuite.this, channel); break;
case 10: test = new TestTerminals(TCFTestSuite.this, channel); break;
}
active_tests.put(test, channel);
}
}
});
count_total = pending_tests.size() * (NUM_CHANNELS + 1);
channels = new IChannel[NUM_CHANNELS];
Protocol.invokeLater(new Runnable() {
public void run() {
try {
openChannels(peer);
}
catch (Throwable x) {
errors.add(x);
int cnt = 0;
for (int i = 0; i < channels.length; i++) {
if (channels[i] == null) continue;
if (channels[i].getState() != IChannel.STATE_CLOSED) channels[i].close();
cnt++;
}
if (cnt == 0) listener.done(errors);
}
}
});
}
private void openChannels(IPeer peer) {
listener.progress("Opening communication channels...", count_done, count_total);
for (int i = 0; i < channels.length; i++) {
final IChannel channel = channels[i] = peer.openChannel();
channel.addChannelListener(new IChannel.IChannelListener() {
public void onChannelOpened() {
for (int i = 0; i < channels.length; i++) {
if (channels[i] == null) return;
if (channels[i].getState() != IChannel.STATE_OPEN) return;
}
for (int i = 0; i < channels.length; i++) {
run_controls.put(channels[i], new RunControl(TCFTestSuite.this, channels[i], i));
}
runNextTest();
}
public void congestionLevel(int level) {
}
public void onChannelClosed(Throwable error) {
channel.removeChannelListener(this);
if (error == null && errors.isEmpty() && (!active_tests.isEmpty() || !pending_tests.isEmpty()) && !cancel) {
error = new IOException("Remote peer closed connection before all tests finished");
}
int cnt = 0;
for (int i = 0; i < channels.length; i++) {
if (channels[i] == channel) {
channels[i] = null;
if (error != null && errors.isEmpty()) errors.add(error);
for (Iterator<ITCFTest> n = active_tests.keySet().iterator(); n.hasNext();) {
if (active_tests.get(n.next()) == channel) n.remove();
}
}
if (channels[i] == null) continue;
if ((error != null || active_tests.isEmpty() && pending_tests.isEmpty()) &&
channels[i].getState() != IChannel.STATE_CLOSED) channels[i].close();
cnt++;
}
if (cnt == 0) listener.done(errors);
}
});
}
}
public void cancel() {
cancel = true;
if (canceled) return;
for (final ITCFTest t : active_tests.keySet()) {
if (t instanceof TestRCBP1) {
((TestRCBP1)t).cancel(new Runnable() {
public void run() {
assert active_tests.get(t) == null;
cancel();
}
});
return;
}
}
canceled = true;
for (IChannel c : channels) {
if (c != null && c.getState() != IChannel.STATE_CLOSED) c.close();
}
}
public boolean isCanceled() {
return canceled;
}
boolean isActive(ITCFTest test) {
return active_tests.get(test) != null;
}
Collection<ITCFTest> getActiveTests() {
return active_tests.keySet();
}
ITCFTest getActiveTest(IChannel channel) {
for (Map.Entry<ITCFTest,IChannel> e : active_tests.entrySet()) {
if (e.getValue() == channel) return e.getKey();
}
return null;
}
Map<String,String> getCanceledTests() {
return cancel_test_ids;
}
boolean canResume(String id) {
for (RunControl r : run_controls.values()) {
if (!r.canResume(id)) return false;
}
for (ITCFTest t : active_tests.keySet()) {
if (!t.canResume(id)) return false;
}
return true;
}
void done(ITCFTest test, Throwable error) {
assert active_tests.get(test) != null;
if (error != null && !canceled) errors.add(error);
active_tests.remove(test);
listener.progress(null, count_done++, count_total);
if (active_tests.isEmpty()) runNextTest();
}
private void runNextTest() {
while (active_tests.isEmpty()) {
if (cancel || errors.size() > 0 || pending_tests.size() == 0) {
for (IChannel channel : channels) {
if (channel != null && channel.getState() != IChannel.STATE_CLOSED) {
if (errors.size() > 0) channel.terminate(new Exception("Test failed"));
else channel.close();
}
}
return;
}
pending_tests.removeFirst().run();
ITCFTest[] lst = active_tests.keySet().toArray(new ITCFTest[active_tests.size()]);
for (ITCFTest test : lst) test.start();
}
}
}