Brute force concurrency testing
Testing concurrency is hard, just ask anyone who had tried. And tracking down concurrent bugs is even harder.
I just run into concurrency issue with SvnBridge, and I decided that I have better have a smoke test around to prove that there are no obvious concurrency issues with the code.
Since I already have over a hundred integration tests, I thought that this would make a great concurrency test. We know that all the tests are passing. Now let us see if they are all passing together. This is quick & dirty, but it expose at least two bugs so far, one of them the original one that we have seen:
public class ConcurrentActionsTest { [Fact] public void RunAllTestsConcurrentyly() { List<TestData> tests = new List<TestData>(); Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { if (type.IsAbstract) continue; if (type == typeof(ConcurrentActionsTest)) continue; foreach (MethodInfo info in type.GetMethods()) { object[] attributes = info.GetCustomAttributes(typeof(SvnBridgeFactAttribute), true); if (attributes.Length == 0) continue; tests.Add(new TestData((SvnBridgeFactAttribute)attributes[0], info)); } } List<IAsyncResult> results = new List<IAsyncResult>(); List<Exception> errors = new List<Exception>(); ExecuteTestDelegate exec = ExecuteTest; foreach (TestData test in tests) { foreach (ITestCommand command in test.Fact.CreateTestCommands(test.Method)) { IAsyncResult invoke = exec.BeginInvoke(test, command, errors, null, null); results.Add(invoke); } } foreach (IAsyncResult result in results) { result.AsyncWaitHandle.WaitOne(); exec.EndInvoke(result); } if (errors.Count > 0) { StringBuilder sb = new StringBuilder(); foreach (Exception error in errors) { sb.AppendLine(error.ToString()); } throw new Exception(sb.ToString()); } } private delegate void ExecuteTestDelegate(TestData test, ITestCommand command, List<Exception> errors); private void ExecuteTest(TestData test, ITestCommand command, List<Exception> errors) { try { object instance = Activator.CreateInstance(test.Method.DeclaringType); command.Execute(instance); } catch (TargetInvocationException e) { lock (errors) errors.Add(e.InnerException); } catch (Exception e) { lock (errors) errors.Add(e); } } private class TestData { public readonly MethodInfo Method; public readonly SvnBridgeFactAttribute Fact; public TestData(SvnBridgeFactAttribute fact, MethodInfo method) { Fact = fact; Method = method; } } }
As I was writing this post, I figure out one issue, the other would require concurrent debugging...
Comments
Maybe you could shuffle the list of tests before you begin executing them? This would let you expose more bugs as you wouldn't always have the same sequence of tests and hence similar overlapping tests every time.
Of course, it makes the test even more nondeterministic....
Shuffling the tests or any other way of automatic random testing is not a bad idea but on each test run you need to log the random seed or the shuffle order. If you cannot rerun the test exactly as it was ran you cannot recreate the bug which makes is useless for any real debug work.
Only slightly OT, but what would you want the debugger tooling to ideally provide/do (that it doesn't today) to assist with debugging threading/concurrency/parallelism scenarios... If you have any thoughts please share.
The ability to stop all threads except this one.
The ability to view current locks for each thread
The ability to see what a thread is waiting on.
Comment preview