对于我们在TFS 2012构建服务器上的CheckIn期间运行的项目,我们确实有一堆UnitTests。虽然在本地他们表现得非常像预期,但有些奇怪的行为发生在TFS偶尔使用TFS的计时器。最后,我们写了一个类和一个测试来隔离这种行为。
班级:
public class TestTimerOnServer
{
Timer _MyTimer = new Timer(100);
int _CountsToDo = 100;
public int TimerElapsedCounter = 0;
public TestTimerOnServer()
{
_MyTimer.Elapsed += _MyTimer_Elapsed;
}
public void StartCounting(int argNumberOfCounts, int argIntervalInMs)
{
_MyTimer.Stop();
_MyTimer.Interval = argIntervalInMs;
TimerElapsedCounter = 0;
_CountsToDo = argNumberOfCounts;
_MyTimer.Start();
}
void _MyTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (_CountsToDo > TimerElapsedCounter)
TimerElapsedCounter++;
else
_MyTimer.Stop();
}
}
针对它的测试:
private void TestTimerServerBehaviour()
{
TestTimerOnServer ttos = new TestTimerOnServer();
ttos.StartCounting(10, 100); // should lead 1000 ms
// we wait 1100 MS
Thread.Sleep(1100);
Assert.AreEqual(ttos.TimerElapsedCounter, 10,"We should have 10
samples, but only " + ttos.TimerElapsedCounter + " have been generated.!");
ttos.StartCounting(50, 100); // should lead 5000 ms
// we wait 6000 MS
Thread.Sleep(6000);
Assert.AreEqual(ttos.TimerElapsedCounter, 50, "We should have 50
samples, but only " + ttos.TimerElapsedCounter + " have been generated.!");
}
大多数情况下,此测试通过,偶尔会在TFS上失败 - 例如触发事件两次而不是50次。所以问题是我们在这里遇到一般的单元测试设计缺陷,还是在TFS构建服务运行测试时遇到的问题呢?
修改 TFS正在VM上运行 - 这可能是我们问题的根源吗?
EDIT2 - 最终调查结果 问题显然是Testing-Context和VMware的结合。我使用@allen发布的代码编写了一个小应用程序,一个使用相同代码的测试和另一个使用cpu加载的代码:
void CPULoader()
{
var action = new Action(() => { while (true);});
Parallel.Invoke(action, action, action, action, action, action, action, action);
}
相当原始但有效率。
在运行应用程序和CPU-Loader的硬件计算机上导致计时器事件稍后发生,例如1100毫秒而不是1000.到目前为止,这很好,Win7毕竟不是实时系统。
在VMware上,某些组合导致延迟高达5000毫秒,应该有一秒...... - 当然要糟糕得多,但事件仍然发生。
真正的乐趣在运行测试时开始(我已经将代码打包到无限循环中),然后启动CPU-Loader。整个系统非常慢,直到我停止加载CPU并且在测试中之前没有发生事件。
希望这可以给某人一些提示 - 我们通过使用Timer抽象并在单元测试时模拟计时行为来修复我们的测试。作为副作用,测试现在运行得更快:)
答案 0 :(得分:1)
对sleep
的系统调用和亲属经常与信号处理程序交互并提前中止他们的睡眠。这种现象最好记录在Unix sleep
(3)函数中,但我怀疑它也适用于您的API。
你应该通过检查睡觉后的当前时间来防止短暂的睡眠。
通常,您希望尽可能将定时器单元测试与系统时钟分离。我在处理基于队列的计时器和可以在外部调用的心跳功能时有很好的经验,但是你应该总是能够对sleep
的调用和取消时间的函数进行虚拟处理,然后进行测试称实际功能。
答案 1 :(得分:1)
我怀疑是在虚拟机上。使用VM,您可能会遇到与时间同步相关的问题,这些问题可能导致与时序相关的功能遇到问题。验证这一点的一种方法是编写一段简单的代码并在同一个VM上运行。
Stopwatch watch = new Stopwatch();
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 30*60 * 1000;
timer.Elapsed += timer_Elapsed;
watch.Start();
timer.Start();
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
watch.Stop();
Console.WriteLine("Stopwatch value - {0}", watch.Elapsed);
watch.Restart();
}
另一个例子是stackoverflow System.Diagnostics.Stopwatch returns negative numbers in Elapsed... properties
可能导致此问题的一些因素是a)VM上禁用的时间同步服务b)主机上缺少资源,例如。虚拟机太多
我的建议是在主机上设置一个新VM,其中有更多专用于此VM的资源