uniset2-testsuiteの概要-機能テスト用の小さなバイク。 パート2





最初の部分は、可能性の概要でした。 そして、このパートでは、どのテストインターフェイスが既に実装されているか、独自のテストインターフェイスを追加する方法について検討します。



どちらかといえば、ここに記事の最初の部分へのリンクがあります



現在、uniset2-testsuiteは3つのインターフェイスをサポートしています。





インターフェイスタイプ=「uniset」



最初の部分で書いたように、 uniset2-testsuite全体は、最初はlibunisetを使用してプロジェクトをテストするために開発されました 。 したがって、 type =“ uniset”は、開発された最初のメインインターフェイスです。 ユニセットプロジェクトでは、主な要素は「センサー」であり、一意の識別子(数値ですが、文字列名を使用できます)と、設定または受信できる値があります。 したがって、テストシナリオでは、すべてがこれらのセンサーとその値のチェックを中心に展開します。 ここで説明されたアルゴリズムを実装する簡単なスクリプトの例です



<?xml version = '1.0' encoding = 'utf-8'?> <TestScenario> <TestList type="uniset"> <test name="Processing" comment="  "> <action set="OnControl_S=1" comment="  ' '"/> <check test="CmdLoad_C=1" comment="  ''"/> <check test="CmdUnload_C=0" comment="  ''"/> <check test="Level_AS>=90" comment=" .." timeout="15000"/> <check test="CmdLoad_C=0" comment="  ''"/> <check test="CmdUnload_C=1" comment="  ''"/> <check test="Level_AS<=10" comment=" .." timeout="15000"/> </test> <test name="Stopped" comment="  "> <action set="OnControl_S=0" comment="  ' '"/> <check test="CmdLoad_C=0" comment=" ''  " holdtime="3000"/> <check test="CmdUnload_C=0" comment=" ''  " holdtime="3000"/> <check test="Level_AS<=80" comment="  " holdtime="10000"/> </test> </TestList> </TestScenario>
      
      







インターフェイスタイプ=「modbus」



このインターフェースは、標準のModbus TCPプロトコルを使用してテスト中のシステムと通信できるため、広範囲の使用にとってより興味深いものです 。 これを使用したテストシナリオは次のとおりです。



 <?xml version = '1.0' encoding = 'utf-8'?> <TestScenario type="modbus"> <Config> <aliases> <item type="modbus" alias="mb1" mbslave="localhost:2048" default="1"/> <item type="modbus" alias="mb2" mbslave="localhost:2049"/> </aliases> </Config> <TestList > <test name="Test simple read"> <check test="0x10!=0"/> <check test="0x24:0x04!=0"/> <check test="0x24!=0"/> <check test="0x1@0x02:0x02!=0" config="mb2"/> <check test="0x24:0x06!=0"/> </test> <test name="Test simple write"> <action set="0x25=10"/> <action set="0x25:0x10=10"/> <action set="0x25@0x02=10" config="mb2"/> <action set="0x25@0x02:0x05=10" config="mb2"/> </test> <test name="Test other 'check'"> <check test="0x20=0x20" timeout="1000" check_pause="100"/> <check test="0x20=0x20"/> </test> <test name="Test 'great'"> <action set="109=10"/> <check test="109>=5"/> <check test="109>=10"/> </test> <test name="Test 'less'"> <action set="109=10"/> <check test="109<=11"/> <check test="109<=10"/> </test> <test name="Test other 'action'"> <action set="20@2=1,21@0x02:5=1,103@2:0x10=12" config="mb2"/> </test> </TestList> </TestScenario>
      
      





最初に、 構成セクションで、作業が実行される2つのノードのパラメーターが定義されます。 条件付きで、それらにはmb1およびmb2という名前が割り当てられます。 それらの1つがデフォルトとして割り当てられますdefault = "1" 。 したがって、テキストのどこでも、パラメーターconfig = ".."が指定されていない場合、デフォルトのノードが使用されます。

テスト記録形式自体は次のようになります。

"mbreg@mbaddr:mbfunc:nbit:vtype"





どこで:

mbadrrmbfuncnbit 、およびvtypeフィールドはオプションであり、デフォルト値があることに注意してください。

その結果、このインターフェイスを使用すると、多くのデバイスを操作するためのテストスクリプトを作成できます。 たとえば、一部のテストリグを管理して、テストアクションをデバイスに送信し、その反応を確認できます。 これはすべて、Modbus TCPプロトコルに準拠しています。これは、ACSだけでなく、ACSで使用されるデバイスのほぼすべてのメーカーによってサポートされています。



インターフェイスタイプ=「snmp」



このインターフェイスは、SNMPプロトコルを介してテスト対象のシステムで動作するように設計されています。

いくつかの実装の詳細
Pythonモジュールpysnmp、netsnmpに基づいた実装がいくつかありました。 しかし、最終的には、さまざまなプラットフォームのさまざまなバージョンで発生した問題により、

より多くの「オーク」ソリューション。 つまり、 net-snmp-clientsファミリーのユーティリティへの呼び出しの使用とpopenによる結果の処理。 逆説的に聞こえるかもしれませんが、このメソッドは純粋なpythonモジュールを使用するよりも「ポータブル」であることが判明しました。



snmpを使用したテストケースの例を以下に示します。

 <?xml version="1.0" encoding="utf-8"?> <TestScenario type="snmp"> <Config> <aliases> <item type="snmp" alias="snmp1" snmp="conf/snmp.xml" default="1"/> <item type="snmp" alias="snmp2" snmp="conf/snmp2.xml"/> </aliases> </Config> <TestList> <test name="SNMP read tests" comm="  snmp"> <check test="uptime@node1>1" comment="Uptime"/> <check test="uptimeName@node2>=1" comment=" " config="snmp2"/> </test> <test name="SNMP: FAIL READ" ignore_failed="1"> <check test="sysServ2@node2>=1" comment="fail read test" config="snmp2"/> </test> <test name="SNMP write tests" comm="  snmp"> <action set="sysName@ups3=10" comment="save sysName"/> <action set="sysServ2@ups3=10" comment="save sysServ"/> </test> <test name="SNMP: FAIL WRITE" ignore_failed="1"> <action set="sysServ2@node1=10" comment="FAIL SAVE TEST"/> </test> </TestList> </TestScenario>
      
      





このインターフェイスを使用するには、特別な構成ファイルが必要です。これは、 Configセクションに示されていますsnmp = ".." )。

SNMPスクリプトのSnmp.xml構成ファイル
 <?xml version='1.0' encoding='utf-8'?> <SNMP> <Nodes defaultProtocolVersion="2c" defaultTimeout='1' defaultRetries='2' defaultPort='161'> <item name="node1" ip="192.94.214.205" comment="UPS1" protocolVersion="2" timeout='1' retries='2'/> <item name="node2" ip="test.net-snmp.org" comment="UPS2"/> <item name="node3" ip="10.16.11.3" comment="UPS3"/> </Nodes> <MIBdirs> <dir path="conf/" mask="*.mib"/> <dir path="conf2/" mask="*.mib"/> </MIBdirs> <Parameters defaultReadCommunity="demopublic" defaultWriteCommunity="demoprovate"> <item name="uptime" OID="1.3.6.1.2.1.1.3.0" r_community="demopublic"/> <item name="uptimeName" ObjectName="sysUpTime.0"/> <item name="bstatus" OID="1.3.6.1.2.1.33.1.2.1.0" ObjectName="BatteryStatus"/> <item name="btime" OID=".1.3.6.1.2.1.33.1.2.2.0" ObjectName="TimeOnBattery"/> <item name="bcharge" OID=".1.3.6.1.2.1.33.1.2.4.0" ObjectName="BatteryCharge"/> <item name="sysName" ObjectName="sysName.0" w_community="demoprivate" r_community="demopublic"/> </Parameters> </SNMP>
      
      







Nodesセクションは、交換が行われるノード(デバイス)のリストを設定します。 この場合、次のパラメーターを設定できます。

[ ノード]セクションで直接、すべてのノードのデフォルトパラメータを設定できます。



MIBdirsセクションでは、mibファイルを含むディレクトリが設定され、OIDの正確性がチェックされます。



Parametersセクションでは、テストに参加するパラメーターのリストを設定します。



OIDパラメーターとObjectNameパラメーターは交換可能です。 両方のパラメーターが指定されている場合、OIDが使用されます。 [ パラメータ]セクションで、すべてのノードのデフォルトパラメータを直接設定できます。





1つのシナリオで複数のインターフェイスを使用する



例を示します。

 <?xml version="1.0" encoding="utf-8"?> <TestScenario> <Config> <aliases> <item type="uniset" alias="u" confile="configure.xml" default="1"/> <item type="modbus" alias="mb" mbslave="localhost:2048"/> <item type="snmp" alias="snmp" snmp="conf/snmp.xml"/> </aliases> </Config> <RunList after_run_pause="5000"> ... </RunList> <TestList> <test name="check: Equal test"> <action set="111=10"/> <check test="111=10"/> <check test="uptime@node1>1" config="snmp"/> <check test="0x10!=0" config="mb"/> </test> </TestList> </TestScenario>
      
      





つまり チェックごとに、 config = "aliasname"を設定して、使用するインターフェイスを指定できます。



インターフェースを実装する方法



入門



基本的なインターフェイスの作業中に、uniset2-testsuiteに埋め込むためのテストインターフェイスの最小APIに関するビジョンが形成されました。 そして、主な機能については2つの機能を実装するだけで十分であることがわかりました( まあ、ほぼ

  def get_value(self, name, context): ... def set_value(self, name, value, context): ...
      
      





これは単純なアイデアに基づいています。 テストシナリオを見ると、一般的に、チェックまたはアクションは次のように記述できることがわかります。

heck="[NAME]=[VALUE]"





「=」の代わりに実際にはこれらの1つが[=、>、> =、<、<= ,! =]である場合があります。

つまり チェックするパラメーターの名前として特定の[NAME]があります。 そして、特定のインターフェースはそれを解析する方法を知っています。 [VALUE]があります-これは、結果を比較する値または設定する値です。 その結果、uniset2-testsuiteは、NAMEおよびVALUEのテストを解析するタスクを引き受け、特定のインターフェイス実装のget_valueまたはset_value関数を呼び出します。 同時に、インターフェイス自体は、[NAME]フィールドで作業が実行されるパラメーターの暗号化方法を担当します。 例:



つまり インターフェース開発者は、 NAMEのどのフォーマットを選択するかを決定します。

現在の実装では、 timeoutcheck_timeholdtimeなどのテストケースパラメータがuniset2-testsuiteレベルで処理されることに留意することが重要です。 したがって、テストで<check test="varname=34" timeout="15000" check_time="3000"/>



と示されている場合、これはget_value(varname)関数がタイムアウトが期限切れになるまで3秒ごとに呼び出されることを意味します34の値が取得されます。

もう1つの重要な制限は、現在の実装では、[VALUE]が数値としてのみサポートされていることです。 実際、「任意の型」( 基本的に文字列 )をサポートするためにリメイクすることはそれほど難しくありません。VALUEサポートを数値として実装する必要はありませんでした。 型compareのチェックがあることを思い出させてください 。これにより、数値ではなく、別のパラメーターの値と比較できます。



構成



各インターフェース自体が、動作に必要な構成パラメーターを決定します。 uniset2-testsuiteでは、それらを記述するためのConfig / aliasesセクションが提供されます。ここでは、パラメーターをxmlプロパティとして書き込むことができます。 インターフェースを作成すると、構成ノードが彼に転送され、そこから必要なものすべてが考慮されます。 余分なものすべてを定義する必要がある場合、たとえば、構成ノードのsnmpインターフェースで、構成ファイル(snmp.xml)を取得する場所のみを決定でき、インターフェースが機能するために必要なものはすべてそこですでに定義されています。 同様に、たとえばmodbusインターフェースの場合、デバイスとの通信用にIPとポートを定義するだけで十分であり、これらのパラメーターはConfigセクションに直接書き込まれます。



プラグイン(インターフェイス)をダウンロードする



インターフェースのロードは、単純な原則に基づいています。 ディレクトリ ' plugins.d 'があり、そこからすべてのインターフェースがロードされます。 「システム」ディレクトリがあり、プラグインはテストが実行されている現在のディレクトリのplugins.dサブディレクトリでも検索されます。 したがって、ユーザーはテストが配置されている場所にプラグインを配置するだけで、プラグインは自動的に取得されます。

インターフェースの各実装は、個別のpythonファイルとして作成されます。このファイルには、 uts_plugin_name()関数が含まれている必要あります。 たとえば、SNMPインターフェイスでは次のようになります

 def uts_plugin_name(): return "snmp"
      
      





その結果、インターフェイス自体のロードは次のメカニズムに基づいています。

 .. <Config> <aliases> <item type="uniset" alias="u" confile="configure.xml" default="1"/> <item type="modbus" alias="mb" mbslave="localhost:2048"/> <item type="snmp" alias="snmp" snmp="conf/snmp.xml"/> </aliases> </Config> ...
      
      





テストスクリプトの処理を開始すると、uniset2-testsuiteは使用可能なプラグインのリストを名前でコンパイルし、 plugins.dディレクトリからロードします。 渡すとき次

type =“ xxxx”はConfigセクションを調べ、対応するインターフェースが検索されます。

特別な関数を呼び出すものを作成するには

xmlノードが渡されるuts_create_from_xml(xmlConfNode)





パラメータとして。 さらに、インターフェースの作成とその初期化をすでに実装しています。

要するに:

 uts_plugin_name() -       type="..." uts_create_from_xml(xmlConfNode) -   
      
      







構成パラメーターの検証



インターフェイスを実装するには、2つの関数set_value()get_value()のみを実装すれば十分であると上記で書きました。 実際、さらに2つ実装することが望ましい(ただし、必須ではない):

  def validate_parameter(self, name): ... def validate_configuration(self): ...
      
      





これらは、-check-scenarioモードの場合に必要です。テストモードの正確性が実際に実行されずにチェックされる場合。 validate_parameter- 名前パラメーターを検証するために呼び出されます。 そして、テスト内の各テストに対して呼び出されることに注意してください。 validate_configurationは、テスト全体の構成パラメーターを確認するために1回呼び出されます。 たとえば、snmp-interfaceでは、ノードの可用性、およびmibファイルによるOIDとObjectNameの有効性を確認します(対応するディレクトリがどこにあるかが示されている場合)。



実装



さて、最後に、実装に着きました。 実例としてどのインターフェイスを実装するかを考えて、可能なインターフェイスの「最も普遍的な」、つまり、チェックとしてスクリプトを実行するインターフェイスを開発することにしました。 これを「スクリプト」と呼びます。 「スクリプト」という言葉は、bashだけでなく、「実行できる」すべてを意味することをすぐに注意したいと思います。 なぜなら テストごとにシェル介してプログラムを実際に実行ます。



だから。

まず、NAMEの形式を考え出す必要があります(上記を参照)。 少し考えて、 正直に言って、試してみたところ 、この単純な形式になりました。

  <test name="check scripts"> <check test="scriptname.sh >= VALUE" params="param1 param2 param3" ../> <check test="../myscritps/scriptname.sh >= VALUE" params="param1 param2 param3" ../> .. </test>
      
      





フォーマット検索について
最初に、GETリクエストのような形式を作成したかった: scriptname?param1&param2&param3.





しかし、xmlがあるという制限に遭遇しました。 そして、「 」をそのまま使用することはできません。 「&」アンプとして記述されている場合のみ 。 もちろん、それはすべての便利さをすでに無効にしている。





つまり in test = ".."スクリプト名前が書き込まれます(パスを指定できます)。 なぜなら 結局のところ、スクリプトにパラメーターを渡したいため、特別なフィールド(拡張) params = "..."を導入します



次に、結果取得する方法を理解する必要があります。 なぜなら プログラムが0を返すことは受け入れられます-成功の場合、失敗の場合は「ゼロではない」場合、結果として戻りコードを使用することは、良い考えではないようです。 最終的に、プログラムが結果を含む特別なマーカーを標準出力に出力するのが最も簡単な方法であると判断しました。 つまり 一方では、プログラムが独自のメッセージを表示することを禁止していませんが、他方では、それから結果を取得する必要があります。 マーカーは次の形式の行になります: TEST_SCRIPT_RESULT: VALUE







つまり スクリプトを実行し、出力でマーカーをキャッチして、そこから結果を切り取ります。 ええ、はい、これ以上良いものは思いつきません。



次の質問はエラー処理です。 ここではすべてが標準です。 戻りコード!= 0の場合、エラー。 エラーの詳細として、stderrのプログラムによって表示されたすべてのものを使用します。



グローバルインターフェース設定パラメーター :今のところ、単純なインターフェースには必要ないと考えています。 しかし、私は例えば1つをしました。



これで物語の終わりです。 どうした





次に、実装に進みます...



モジュールをロードするには、2つのグローバル関数uts_create_from_xml(..)uts_plugin_name()を実装する必要があります。

彼らは簡単です
 def uts_create_from_xml(xmlConfNode): """   :param xmlConfNode: xml-   :return:   UTestInterface """ return UTestInterfaceScripts(xmlConfNode=xmlConfNode) def uts_plugin_name(): return "scripts"
      
      









インターフェイス自体は、基本クラスUTestInterfaceから継承し、必要な機能を実装する必要があります。 最初に、UTestInterface.pyクラスがどのように見えるかを示します。

UTestInterface.py
 #!/usr/bin/env python # -*- coding: utf-8 -*- from TestSuiteGlobal import * class UTestInterface(): """  """ def __init__(self, itype, **kwargs): self.itype = itype self.ignore_nodes = False def set_ignore_nodes(self, state): """ set ignore 'node' for tests (id@node) :param state: TRUE or FALSE """ self.ignore_nodes = state def get_interface_type(self): return self.itype def get_conf_filename(self): return '' def validate_parameter(self, name): """ Validate test parameter (id@node) :param name: parameter from <check> or <action> :return: [ RESULT, ERROR ] """ return [False, "(validateParam): Unknown interface.."] def validate_configuration(self): """ Validate configuration parameters (check-scenario-mode) :return: [ RESULT, ERROR ] """ return [False, "(validateConfiguration): Unknown interface.."] def get_value(self, name): raise TestSuiteException("(getValue): Unknown interface..") def set_value(self, name, value, supplier_id): raise TestSuiteException("(setValue): Unknown interface...")
      
      









メイン関数get_value(self, name, context)



実装することから始めget_value(self, name, context)





名前が渡されます-私たちの場合、これは基本的にスクリプトの名前です。

ただし、追加のparamsフィールドが導入されているため、同様に処理する必要があります。

追加のフィールドに到達するために、このような便利なパラメーターをcontextとして使用しますドキュメントに他に興味深いものが書かれていますが、そうでないかもしれないという事実を考慮して、現在のテストのxmlノードを引き出します(そして例外をスローします)。

 def get_value(self, name, context): xmlnode = None if 'xmlnode' in context: xmlnode = context['xmlnode'] if not xmlnode: raise TestSuiteException("(scripts:get_value): Unknown xmlnode for '%s'" % name) ...
      
      





便宜上、名前とコンテキストを入力として受け取り、 scriptnameparametersを返す別の関数(形式が後で変更されるかどうか)を強調しました。



ここにあります:

parse_name()
  @staticmethod def parse_name(name, context): """   : <check test="scriptname=XXX" params="param1 param2 param3" .../> :param name:   (     scriptname) :param context: :return: [scriptname, parameters] """ if 'xmlnode' in context: xmlnode = context['xmlnode'] return [name, uglobal.to_str(xmlnode.prop("params"))] return [name, ""]
      
      







次に、get_value()は



完全に実装するだけです

get_value()
 def get_value(self, name, context): xmlnode = None if 'xmlnode' in context: xmlnode = context['xmlnode'] if not xmlnode: raise TestSuiteException("(scripts:get_value): Unknown xmlnode for '%s'" % name) scriptname, params = self.parse_name(name, context) if len(scriptname) == 0: raise TestSuiteException("(scripts:get_value): Unknown script name for '%s'" % name) test_env = None if 'environment' in context: test_env = context['environment'] s_out = '' s_err = '' cmd = scriptname + " " + params try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=test_env, close_fds=True, shell=True) s_out = p.stdout.read(self.max_read) s_err = p.stderr.read(self.max_read) retcode = p.wait() if retcode != 0: emessage = "SCRIPT RETCODE(%d) != 0. stderr: %s" % (retcode, s_err.replace("\n", " ")) raise TestSuiteException("(scripts:get_value): %s" % emessage) except subprocess.CalledProcessError, e: raise TestSuiteException("(scripts:get_value): %s for %s" % (e.message, name)) if xmlnode.prop("show_output"): print s_out ret = self.re_result.findall(s_out) if not ret or len(ret) == 0: return None lst = ret[0] if not lst or len(lst) < 1: return None return uglobal.to_int(lst)
      
      







少し説明。



スクリプトを実行するために、環境変数をスクリプトに渡すこともできました。 これは、スクリプトに関するドキュメントのセクションにあります。

繰り返しますが、コンテキストから環境変数を含む辞書を取得します: test_env = context['environment']







実装に2つの追加パラメーターshow_output = "1"を追加することを許可しました-これはチェックレベルパラメーターです。 スクリプトがstdoutに表示するすべての画面への出力を含める。 デフォルトでは、スクリプトは出力を無効にして実行されます。 そして、入力した2番目のパラメーターはグローバルなmax_read設定です。これは、結果を取得するために読み取る必要がある出力の(最初の)バイト数を決定します。 まず、グローバル設定の使用方法を示す必要がありました。 第二に、読み取り用のバッファを制限することは良い考えだと思いました。 さらに、サイズが0以下に設定されている場合は、すべて実行できます。

リターンコードの処理で、コードがnullでない場合、エラーテキストとしてstderrを使用します(同じ行に沿って作成します)。

正規表現を使用したParsimの結果。

細部
この場合、実行中のプログラムの完了を待っていることがわかります。 つまり プログラムは非常に簡潔で、

すぐに終了します。 それでも、プログラムが「何時間も続けて」動作した場合は奇妙です。 一方、タイムアウトによってプログラムを強制終了することも可能です。



インターフェイスをテストするには、 plugins.dディレクトリを作成し、そこにモジュールを配置します。 また、小さなテストを書く

インターフェイスをテストするテスト
 <?xml version="1.0" encoding="utf-8"?> <TestScenario> <Config> <environment> <item name="MyTEST_VAR1" value="MyTEST_VALUE1"/> <item name="MyTEST_VAR2" value="MyTEST_VALUE2"/> <item name="MyTEST_VAR3" value="MyTEST_VALUE3"/> </environment> <aliases> <item type="scripts" alias="s" default="1"/> </aliases> </Config> <TestList type="scripts"> <test name="Test run script" ignore_failed="1"> <check test="./test-script.sh != 10" params="--param1 3 --param2 4" timeout="2000"/> <check test="./test-script.sh = 100" params="param1=3,param2=4"/> <check test="./test-script-negative-number.sh = -20" show_output="1"/> <check test="./test-script-longtime.sh = 100" timeout="3000"/> <check test="./test-script-error.sh > 10"/> </test> </TestList> </TestScenario>
      
      







Configセクションでは、デフォルトのインターフェイスタイプが「scripts」であることが示されていることに注意してくださいTestListタグでは、スクリプト全体がこのタイプであることも示されています。

スクリプトに加えて、次のような指定されたbashスクリプトを作成することを忘れないでください。

test-script.sh
 #!/bin/sh echo "TEST SCRIPT: $*" echo "SHOW OUTOUT..." echo "TEST_SCRIPT_RESULT: 100"
      
      







同時に、環境変数を導出してそれらを調べます

test-script-negative-number.sh
 #!/bin/sh echo "TEST SCRIPT: $*" echo "SHOW ENV VARIABLES: .." env | grep MyTEST env | grep UNISET_TESTSUITE echo "TEST_SCRIPT_RESULT: -20"
      
      









その結果、我々は(パラメータを含む画像が表示されます私たちのスクリプトを実行した場合show_output =«1»をテストでのスクリプトやパラメータignore_failed =«1»の一つでは):

実行するコマンド
 uniset2-testsuite-xmlplayer --testfile tests-scripts-interface.xml --log-show-actions --log-show-tests
      
      











しかし、それだけではありません。これまで、1つの関数のみを実装しましたget_value(...)



あといくつか残っています。



支援活動はどうですか?つまりset_value()関数の実装...

私たちのインターフェースでは、サポートを行う意味がないと判断しました
 <action set="..."/>
      
      



定期的なメカニズムがあるため
 <action script=".."/>
      
      





したがって、関数は次のようになります。

set_value(..)
  def set_value(self, name, value, context): raise TestSuiteException("(scripts:set_value): Function 'set' is not supported. Use <action script='..'> for %s" % name)
      
      









さらに進んでください...

testsuiteには--check-scenarioモードがあります。このモードでは、実際に実行することなく、設定とテストの正確性がチェックされます。このモードをサポートするにはvalidate_parametervalidate_configurationの 2つの関数を実装する必要がありますmax_output_readを除き、グローバル構成パラメーターがないため、構成をチェックインするための特別なものはありません。したがって、validate_configuration関数は何もしません。しかしvalidate_parameterもう少し面白いです。実際、スクリプトによると、(実際に実行せずに)検証できる最大値は次のとおりです。



これはすべて実装に反映されます。

validate_parameter()およびvalidate_configuration()の実装
  def validate_configuration(self, context): return [True, ""] def validate_parameter(self, name, context): """ :param name: scriptname :param context: ... :return: [Result, errors] """ err = [] xmlnode = None if 'xmlnode' in context: xmlnode = context['xmlnode'] scriptname, params = self.parse_name(name, context) if not scriptname: err.append("(scripts:validate): ERROR: Unknown scriptname for %s" % str(xmlnode)) if not is_executable(scriptname): err.append("(scripts:validate): ERROR: '%s' not exist" % scriptname) if len(err) > 0: return [False, ', '.join(err)] return [True, ""]
      
      







エラー出力の例として、スクリプトに「存在しない」スクリプトへの呼び出しを挿入しました。

調整されたシナリオ
 <?xml version="1.0" encoding="utf-8"?> <TestScenario> <Config> <environment> <item name="MyTEST_VAR1" value="MyTEST_VALUE1"/> <item name="MyTEST_VAR2" value="MyTEST_VALUE2"/> <item name="MyTEST_VAR3" value="MyTEST_VALUE3"/> </environment> <aliases> <item type="scripts" alias="s" default="1"/> </aliases> </Config> <TestList type="scripts"> <test name="Test run script" ignore_failed="1"> <check test="./test-script.sh != 10" params="--param1 3 --param2"/> <check test="./test-script.sh = 100" params="param1=3,param2=4"/> <check test="./test-script-negative-number.sh = -20" show_output="1"/> <check test="./test-script-longtime.sh = 100" timeout="3000"/> <check test="./test-script-error.sh > 10"/> <check test="./non-existent-script.sh > 10"/> </test> </TestList> </TestScenario>
      
      







また、スクリプトチェックを実行すると、次のようになります

実行するコマンド
 uniset2-testsuite-xmlplayer --testfile tests-scripts-interface.xml --log-show-actions --log-show-tests --check-scenario
      
      











さて、これですべてを追加し、インターフェイスの実装の最終バージョンを確認できます。

UTestInterfaceScripts.py
 #!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import re import subprocess from UTestInterface import * import uniset2.UGlobal as uglobal class UTestInterfaceScripts(UTestInterface): """      .  : <check test="testscript=VALUE" params="param1 param2 param3.." show_output="1".../> :         (stdout)  TEST_SCRIPT_RESULT: VALUE :    !=0    !         0.  : show_output=1 -    stdout..    ( <Config>): max_output_read="value" -        ,   .  : 1000 """ def __init__(self, **kwargs): """ :param kwargs:  """ UTestInterface.__init__(self, 'scripts', **kwargs) self.max_read = 1000 if 'xmlConfNode' in kwargs: xmlConfNode = kwargs['xmlConfNode'] if not xmlConfNode: raise TestSuiteValidateError("(scripts:init): Unknown confnode") m_read = uglobal.to_int(xmlConfNode.prop("max_output_read")) if m_read > 0: self.max_read = m_read self.re_result = re.compile(r'TEST_SCRIPT_RESULT: ([-]{0,}\d{1,})') @staticmethod def parse_name(name, context): """   : <check test="scriptname=XXX" params="param1 param2 param3" .../> :param name:   (     scriptname) :param context: :return: [scriptname, parameters] """ if 'xmlnode' in context: xmlnode = context['xmlnode'] return [name, uglobal.to_str(xmlnode.prop("params"))] return [name, ""] def validate_configuration(self, context): return [True, ""] def validate_parameter(self, name, context): """ :param name: scriptname :param context: ... :return: [Result, errors] """ err = [] xmlnode = None if 'xmlnode' in context: xmlnode = context['xmlnode'] scriptname, params = self.parse_name(name, context) if not scriptname: err.append("(scripts:validate): ERROR: Unknown scriptname for %s" % str(xmlnode)) if not is_executable(scriptname): err.append("(scripts:validate): ERROR: '%s' not exist" % scriptname) if len(err) > 0: return [False, ', '.join(err)] return [True, ""] def get_value(self, name, context): xmlnode = None if 'xmlnode' in context: xmlnode = context['xmlnode'] if not xmlnode: raise TestSuiteException("(scripts:get_value): Unknown xmlnode for '%s'" % name) scriptname, params = self.parse_name(name, context) if len(scriptname) == 0: raise TestSuiteException("(scripts:get_value): Unknown script name for '%s'" % name) test_env = None if 'environment' in context: test_env = context['environment'] s_out = '' s_err = '' cmd = scriptname + " " + params try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=test_env, close_fds=True, shell=True) s_out = p.stdout.read(self.max_read) s_err = p.stderr.read(self.max_read) retcode = p.wait() if retcode != 0: emessage = "SCRIPT RETCODE(%d) != 0. stderr: %s" % (retcode, s_err.replace("\n", " ")) raise TestSuiteException("(scripts:get_value): %s" % emessage) except subprocess.CalledProcessError, e: raise TestSuiteException("(scripts:get_value): %s for %s" % (e.message, name)) if xmlnode.prop("show_output"): print s_out ret = self.re_result.findall(s_out) if not ret or len(ret) == 0: return None lst = ret[0] if not lst or len(lst) < 1: return None return uglobal.to_int(lst) def set_value(self, name, value, context): raise TestSuiteException( "(scripts:set_value): Function 'set' is not supported. Use <action script='..'> for %s" % name) def uts_create_from_args(**kwargs): """   :param kwargs:   :return:   UTestInterface """ return UTestInterfaceScripts(**kwargs) def uts_create_from_xml(xmlConfNode): """   :param xmlConfNode: xml-   :return:   UTestInterface """ return UTestInterfaceScripts(xmlConfNode=xmlConfNode) def uts_plugin_name(): return "scripts"
      
      









まとめ



uniset2-testsuiteは単純な自転車で、ほとんどの場合、テストが「適用、反応を確認」というスキームに適合していれば十分ですテスト用のメカニズムの最小セットが存在します。



さらに、プラグインシステムの存在により、必要に応じて機能を簡単に拡張し、使用可能なプロトコル(REST APIまたはRS485)を使用してテストシステムとの対話インターフェイスを実装できます。いくつかの機能を実装するだけです。

小さな追加
- python . C++. , python - . .





関連リンク:




All Articles