4

私は現在、COMクライアントAPI(TDApiOle80またはTDApiOle80.TDConnectionと呼ばれることが多い)を介してHP Quality Center 10.0と統合する.Netベースのソフトウェア(.Net Framework 3.5 SP1)に取り組んでいます。

XUnit1.6.1.1521とGallio3.1.397.0(msbuildファイルから呼び出されます)を使用しています

次のプロセスを実行します。

  • 接続の作成
  • テストの実行
  • 接続を閉じる
  • 処分
  • GC.Collection()/ GC.AwaitingPendingFinalizers()を強制する

統合テストごとに-そして各統合テストは、ファクトで構成されたタイムアウトを使用して実行されます。

私たちが抱えている問題は、いくつかのテスト(たとえば約10回)の後にQuality Centerが呼び出されると無期限にブロックされ、Gallio全体がフリーズして応答しなくなることです。

当初、xunit.netはファクト内のコードにのみタイムアウトを適用することを発見しました-したがって、コンストラクターまたはdisposeメソッドが完了するまで無期限に待機します-確認のために、そのロジックをテストの本体に移動しました...しかしこれは問題を解決していません(特定の数のテストを実行した後もハングします)。

TestDriven.Netを使用する場合も同じことが起こります。1つまたはいくつかのテストをインタラクティブに実行できますが、約10を超えるテストが実行され、実行全体がフリーズします。唯一の選択肢は、TD.Netで使用されるProcessInvocation86.exeプロセスを強制終了することです。

誰かがこれがすべて一緒に起こるのを止める方法、または少なくともこれらの種類の問題から私の統合テストを隔離する方法のいずれかに関するヒント/コツを持っていますか?QC APIが無期限にブロックするテストでは、テストはタイムアウトで失敗し、 Gallioが次のテストに移動できるようにします。

アップデート

STAスレッドを使用するためのヒントは、問題を少し前進させるのに役立ちました。カスタムXUnit.Net属性を介して、独自のSTAスレッドでテストを開始します。これにより、Gallio / TestDriven.Netが完全にロックアップするのを防ぎ、ハドソンビルドサーバーで統合テストを実行することを含めることができます。

    public class StaThreadFactAttribute : FactAttribute
    {
        const int DefaultTime = 30000; // 30 seconds

        public StaThreadFactAttribute()
        {
            Timeout = DefaultTime;
        }

        protected override System.Collections.Generic.IEnumerable<Xunit.Sdk.ITestCommand> EnumerateTestCommands(Xunit.Sdk.IMethodInfo method)
        {
            int timeout = Timeout;

            Timeout = 0;

            var commands = base.EnumerateTestCommands(method).ToList();

            Timeout = timeout;

            return commands.Select(command => new StaThreadTimeoutCommand(command, Timeout, method)).Cast<ITestCommand>();
        }
    }

    public class StaThreadTimeoutCommand : DelegatingTestCommand
    {
        readonly int _timeout;
        readonly IMethodInfo _testMethod;

        public StaThreadTimeoutCommand(ITestCommand innerComand, int timeout, IMethodInfo testMethod)
            : base(innerComand)
        {
            _timeout = timeout;
            _testMethod = testMethod;
        }

        public override MethodResult Execute(object testClass)
        {
            MethodResult result = null;

            ThreadStart work = delegate
                                                    {
                                                        try
                                                        {
                                                            result = InnerCommand.Execute(testClass);
                                                            var disposable = testClass as IDisposable;
                                                            if (disposable != null) disposable.Dispose();
                                                        }
                                                        catch (Exception ex)
                                                        {
                                                            result = new FailedResult(_testMethod, ex, this.DisplayName);
                                                        }
                                                    };

            var thread = new Thread(work);

            thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA

            thread.Start();

            if (!thread.Join(_timeout))
            {
                return new FailedResult(_testMethod, new Xunit.Sdk.TimeoutException((long)_timeout), base.DisplayName);
            }

            return result;
        }
    }

代わりに、TestDriven.Netでテストを実行すると、次のような出力が表示されます。偶然に同じスイートを数回実行すると、すべてのテストが合格するか、通常は1つまたは2つのテストが失敗します。そして、最初の失敗の後、2番目の失敗はこの「appdomainのアンロード中のエラー」の問題を引き起こします。

テスト'IntegrationTests.Execute_Test1'が失敗しました:テスト実行時間が超過しました:30000ms

テスト'T:IntegrationTests.Execute_Test2'が失敗しました:appdomainのアンロード中にエラーが発生しました。(HRESULTからの例外:0x80131015)System.CannotUnloadAppDomainException:appdomainのアンロード中にエラーが発生しました。(HRESULTからの例外:0x80131015)System.AppDomain.Unload(AppDomain domain)at Xunit.ExecutorWrapper.Dispose()at Xunit.Runner.TdNet.TdNetRunner.TestDriven.Framework.ITestRunner.RunMember(ITestListenerリスナー、アセンブリアセンブリ、MemberInfoメンバー)at TestDriven.TestRunner.AdaptorTestRunner.Run(ITestListener testListener、ITraceListener traceListener、String assemblyPath、String testPath)at TestDriven.TestRunner.ThreadTestRunner.Runner.Run()

4が合格、2が失敗、0がスキップ、50.42秒(xunit)かかりました。

Quality Center APIがランダムに無期限にハングしている理由はまだわかりません。これについては、まもなくさらに調査します。

2010年7月27日更新

私はついにハングの原因を突き止めました-問題のあるコードは次のとおりです。

connection = new TDConnection();
connection.InitConnectionEx(credentials.Host);
connection.Login(credentials.User, credentials.Password);
connection.Connect(credentials.Domain, credentials.Project);
connection.ConnectProjectEx(credentials.Domain, credentials.Project, credentials.User, credentials.Password);

Connectを呼び出してからConnectProjectExを呼び出すと、ブロックされる可能性があるようです(ただし、決定論的ではありません)。冗長な接続呼び出しを削除すると、テストの安定性が劇的に向上したようです-正しい接続コード:

connection = new TDConnection();
connection.InitConnectionEx(credentials.Host);
connection.ConnectProjectEx(credentials.Domain, credentials.Project, credentials.User, credentials.Password);

コードベースを継承したので、接続コードについてはあまり考えませんでした。

私がまだ理解していないことの1つは、上記のタイムアウトコードを使用しても、Thread.Join(timeout)が返されない理由です。デバッガーを接続すると、テストスレッドが参加/待機操作中であることが示されます。おそらく、STAスレッドでの実行と何か関係がありますか?

4

1 に答える 1

1

別のスレッドでコードを実行してから、タイムアウトを指定して新しいスレッドを呼び出しJoin、タイムアウトに達した場合は中止することができます。

例えば:

static readonly TimeSpan Timeout = TimeSpan.FromSeconds(10);
public static void RunWithTimeout(ThreadStart method) {
    var thread = new Thread(method);
    thread.Start();
    if (!thread.Join(Timeout)) {
        thread.Abort();
        Assert.False(true, "Timeout!");
}
于 2010-07-19T02:48:06.247 に答える