Per Bothner
<per@bothner.com>
mailto:srfi-64@srfi.schemers.org
.
See instructions
here to subscribe to the list. You can access previous messages via
the
archive of the mailing list.
この SRFI は test suite を記述するための API を定義する。 これにより Scheme の API、ライブラリ、アプリケーション、実装系を ポータブルにテストすることが容易にできるようになる。 test suite は test runner のもとで 実行される test case の集合である。 本仕様ではさらに新しい test runner を作成できるようにし、 test suite の実行結果の報告と処理をカスタマイズできるようにする。
Scheme コミュニティは test suite を記述するための標準を必要としている。 それぞれの SRFI、ないしはライブラリには test suite が用意されているべきである。 そのような test suite は、移植性があり、モジュールのような非標準の機能を 必要としないものでなければならない。 test suite の実装や runner には移植性がある必要はない。 しかし、移植性のある基本的な実装を書けることが望ましい。
Scheme で書かれた testing framework には、例えば SchemeUnit がある。 しかし、SchemeUnit には移植性がなく、また、少しく冗長でもある。 本仕様の枠組みと SchemeUnit との間にブリッジを用意すれば 相互のテストを実行でき有用であるかもしれない。 また、Java 用の「標準的な」JUnit API の Scheme インタフェースが少なくともひとつ存在する。 こういったものとのブリッジを用意すれば、JUnit の test runner のもとで テストを実行でき有用であるかもしれない。 しかし、これらの機能は本仕様の範囲外である。
本仕様の API では暗黙の test runner のような明示されない動的状態をあつかう。 これにより、API が使いやすく簡潔になるが、一方で、 JUnit 風のフレームワークのように明示的なテストオブジェクトをあつかうのに 比べて多少エレガントさを欠き合成的でなくなるかもしれない。 これはオブジェクト指向と関数型のどちらかの設計理念に従ったというわけではない、 しかし、実用的で使いやすく拡張しやすいものであろうと考えている。
本提案により、いくつかのマクロを加えるだけで Scheme ソースファイルを test suite にすることができるようになる。 ファイル全体を新しい形式で書く必要はないため、字下げをしなおす必要もない。
本 API で定義される名前はすべて test-
で始まり、
関数風のフォームはすべて構文として定義してある。
これらは関数やマクロ、組み込み構文として実装してもよい。
これらを構文と指定したのは、特定のテストを、部分式を評価せずにとばしたり、
実装側で行番号を表示する機能や例外を補足する機能を追加させられるように
するためである。
本仕様はいくらか複雑な使用ではあるが、最初の何節かを読めば 単純な tesut suite を記述できるようになるだろう。 特別の test runner を記述するといったような、より高度な機能は この仕様書の最後にある。
まずは単純な例から始めよう。 これは完全な自己完結型 test suite である。
;; test suite に名前をつけ初期化を行う (test-begin "vec-test") (define v (make-vector 5 99)) ;; 真になるかテスト (test-assert (vector? v)) ;; eqv? になるかテスト (test-eqv 99 (vector-ref v 2)) (vector-set! v 2 7) (test-eqv 7 (vector-ref v 2)) ;; test suite を終了して結果を報告 (test-end "vec-test")
この test suite はソースファイルとして保存され、 ほかに必要なものは何もない。トップレベルのフォームも必要ない。 したがって、字下げを追加することなしに、既存のプログラムやテストを 簡単にこの形式に変換することができる。 また、新規にテスト追加するのも簡単で、個々のテストに名前をつける必要がない (名前はオプションである)。
test case は test runner のもとで実行される。
test runner はテストの実行結果を蓄積し、報告するオブジェクトである。
本仕様では必要に応じた test runner を作成し使用する方法を定義しているが、
それだけでなく、実装系はデフォルトの test runner も提供しなければならない。
また、提案として(必須ではないが)、上記のファイルをトップレベル環境に
読み込むと、実装ごとに指定されたデフォルトの test runner
をつかってテストを実行し、test-end
により、
実装ごとに指定された方法で結果の概要が表示されることが提案されている。
もっとも基礎的な test case は
与えられた条件が真であるかどうかをテストするものである。
それぞれに名前をつけることもできる。
中心的な test case 形式は test-assert
である。
(test-assert [test-name] expression)
expression を評価し、
その結果が真であった場合はテストが成功し、
偽であった場合にはテストの失敗を報告する。
このテストは、実装系に例外を補足する機能があれば、
例外が発生した場合にも失敗をする。
どのように失敗を報告するかは test runner の環境に依存する。
test-name は test case の名前をあらわす文字列である
(上記の例では test-name は文字列リテラルであるが、式でもよく、
ただ一度だけ評価される)。
この値はエラーの報告や、下で述べる、テストを無視するときに使われる。
現在の test runner のない状態で test-assert
を起動すると
エラーになる。
以下の形式の方が test-assert
を直接使うよりも使いやすいかもしれない。
(test-eqv [test-name] expected test-expr)
これは以下と等価である。
(test-assert [test-name] (eqv? expected test-expr))
同様に、test-equal
と test-eq
はそれぞれ test-assert
と
equal?
、eq?
を組み合わせた略記法である。
(test-equal [test-name] expected test-expr) (test-eq [test-name] expected test-expr)
簡単な例を挙げる。
(define (mean x y) (/ (+ x y) 2.0)) (test-eqv 4 (mean 3 5))
不正確な実数の近似比較には
test-approximate
を使うことができる。
(test-approximate [test-name] expected test-expr error)
これは、(各引き数が一度しか評価されないことを除けば)以下と同等である。
(test-assert [test-name] (and (>= test-expr (- expected error)) (<= test-expr (+ expected error))))
また、評価が失敗すべき場合を示す方法も必要である。 この形式は必要なときにエラーが検出されるかどうかをテストする。
(test-error [[test-name] error-type] test-expr)
test-error
は、評価中にエラーが発生することを期待する形式である。
エラーの種類は error-type で指定する。
error-type を省略した場合、あるいは #t
とした場合、「何らかのエラーが通知されるべき」ことを意味する。
(test-error #t (vector-ref '#(1 2) 9))
本仕様では test-error
の受け取る error-type
の値は実装ごとに(もしくは、将来の仕様で)定義されるものとする。
ただし、どの実装も #t
は受けつけなければならない。
SRFI-35
のコンディション
をサポートする実装もあるが、
SRFI-36 の
I/O condition
についてしか標準化されていず、test suite
を書くにはほとんど役に立たないだろう。
また実装系に固有の例外型を使える場合もあるだろう。
例えば Java ベースの実装では、Java の例外クラスの名前が使えるかもしれない。
;; Kawa の場合 (test-error <java.lang.IndexOutOfBoundsException> (vector-ref '#(1 2) 9))
実装が例外を補足できない場合は test-error
は無視すること。
構文をテストするのはむづかしい。 不正な構文がエラーを起こして欲しい場合は特に、である。 そういった場合には、この関数が役に立つかもしれない。
(test-read-eval-string string)
この関数は string を(read
をつかって)
読み込み、それを評価した結果が
test-read-eval-string
から返る。
read
が終了した時点で読み込まれていない文字があった場合にはエラーが通知される。
例えば、
(test-read-eval-string "(+ 3 4)")
は評価すると 7
になる。
(test-read-eval-string "(+ 3 4")
はエラーを通知する。
(test-read-eval-string "(+ 3 4) ")
もエラーを通知する、
これは、リストを読み込んだあとに不要な文字(つまり空白文字がひとつ)
残っているからである。
test-read-eval-string
は以下のようにテストで使う。
(test-equal 7 (test-read-eval-string "(+ 3 4)")) (test-error (test-read-eval-string "(+ 3")) (test-equal #\newline (test-read-eval-string "#\\newline")) (test-error (test-read-eval-string "#\\newlin")) ;; SRFI-62 が使えない場合には次のテストふたつは無視する。 (test-skip (cond-expand (srfi-62 0) (else 2))) (test-equal 5 (test-read-eval-string "(+ 1 #;(* 2 3) 4)")) (test-equal '(x z) (test-read-string "(list 'x #;'y 'z)"))
test-group は test case、式、定義の並びに名前をつけたものである。 ある test group に入ると test group name が設定され、 抜けるときに直前の group の名前に戻される。 これは動的に(実行時に)おこなわれ、group にはそれ以外の効果や識別性はない。 test group は非形式的なグループ分けで、Scheme の値や構文形式ではない。
test group 内には test group が入れ子になっている場合がある。 test group path は現在有効な test group name を外側から順に並べたリストである。
(test-begin suite-name [count])
test-begin
は新たな test group のはじまりを示す。
suite-name が現在の test group name になり、
test group path に追加される。
可搬性のある test suite を書く場合には suite-name
は文字列リテラルにすべきである。
式やその他のリテラルを指定した場合の結果は規程されていない。
Rationale: 文字列よりもシンボルの方が好ましい場合もある。 しかし、人間の読みやすい名前をつけたい場合に、Scheme の標準では リテラルシンボルに空白を含めたり大文字小文字を混在させることができない。
省略可能な count はこの group で実行される test case の数に一致しなければならない (このとき、入れ子の test group はひとつの test case として数えられる)。 これは、何らかの予期しないエラーでテストが実行されない場合を補足するのに 役立つかもしれない。
現在実行中の test runner がない場合にはさらに、実装依存の方法でそれを設定する。
(test-end [suite-name])
test-end
は現在の test group の終わりを示す。
suite-name が現在の test group name と一致しない場合には
エラーが通知される。
加えて、対応する test-begin
で test runner
が設定されていた場合には、テスト結果を実装依存の方法で報告したあとに、
test-end
でそれを破棄する。
(test-group suite-name decl-or-expr ...)
以下と等価である。
(if (not (test-to-skip% suite-name)) (dynamic-wind (lambda () (test-begin suite-name)) (lambda () decl-or-expr ...) (lambda () (test-end suite-name))))
基本的に、名前つき test group 内で decl-or-expr
を実行するのと同等である。しかし、test-skip
(後述)に一致した場合にはグループ全体が無視される。
また、例外が発生した場合でも test-end
が実行される。
(test-group-with-cleanup suite-name decl-or-expr ... cleanup-form)
decl-or-expr
を
(<body>
と同じ順序で)実行し、
それから cleanup-form
を実行する。
cleanup-form
は decl-or-expr
の実行中に例外が発生した場合にも実行される
(実装系が例外を補足できることを仮定している)。
例:
(test-group-with-cleanup "test-file" (define f (open-output-file "log")) (do-a-bunch-of-tests f) (close-output-port f))
以下、テストの実行制御や 失敗が期待されるテストを示すための機能について述べる。
ある特定のテストだけを実行したいことや、
ある特定のテストが失敗することが前もってわかっていることがときどきある。
test specifier は test runner
を受けとり、真理値を返す一引き数関数である。
test specifier はテストがおこなわれるまえに実行され、
その結果により、そのテストが実行されるかどうかを制御する。
便利のために、test specifier には手続き以外の値を使うこともでき、
以下の count
や name
のようなものは、test specifier 手続きに変換される。
簡単な例を挙げる。
(if some-condition (test-skip 2)) ;; 次のふたつのテストを無視する。
(test-match-name name)
戻り値の test specifier は(test-runner-test-name
の返した)現在の test name が name と equal?
の場合にマッチする。
(test-match-nth n [count])
状態つき述語に評価される。
すなわち、この述語はカウンタで呼び出し回数を記憶し、
n 回目の呼び出しと(最初の呼び出しは 1
回目である)、次の (- count 1)
回の呼び出しにマッチする。count のデフォルト値は 1
である。
(test-match-any specifier ...)
戻り値の test specifier は specifier
のいずれかがマッチした場合にマッチする。
specifier
は順にすべて適用される。
したがって、先の test specifier が真であったとしても、
以降の test specifier の副作用もかならず起こる。
(test-match-all specifier ...)
戻り値の test specifier は specifier
がすべてマッチした場合にマッチする。
specifier
はすべて順に適用される。
したがって、先の test specifier が偽であったとしても、
以降の test specifier の副作用もかならず起こる。
count
(整数値)
(test-match-nth 1 count)
の略。
name
(文字列)
(test-match-name 1 name)
の略。
場合によって、テストを無視したい場合があるかもしれない。
(test-skip specifier)
test-skip
を評価すると specifier
を評価した結果を現在有効な skip-specifier に追加する。
それぞれのテスト(ないしは test-group
が実行される前に有効な skip-specifier が実行中の test runner
に適用され、いずれかの test specifier
がマッチした場合、そのテストは無視される。
便利のために、specifier に文字列が指定されていた場合、
(test-match-name specifier)
の糖衣構文になる。
(test-skip "test-b") (test-assert "test-a") ;; 実行される (test-assert "test-b") ;; 無視される
test-skip
で登録された test specifier
はすべて、以下のように入れ子になっていない test-end
で取り除かれる。
(test-begin "group1") (test-skip "test-a") (test-assert "test-a") ;; 無視される (test-end "group1") ;; skip specifier を取り除く (test-assert "test-a") ;; 実行される
時によって、テストが失敗するとわかっていながら、それを直す時間がなかったり 修正することができないことがある。 また、ある特定の機能は特定のプラットフォームでしか動作しないかもしれない。 とまれ、いつか修正するときのために test case をそのままに、 そのテストが失敗するはずだということを示しておきたいことがある。
(test-expect-fail specifier)
(test-skip
で同様の定義で)specifier
にマッチしたテストが失敗するであろうことを示す。
これは、テストの結果報告のときにだけ影響し、実行には影響を与えない。
(test-expect-fail 2) (test-eqv ...) ;; 失敗するはず (test-eqv ...) ;; 失敗するはず (test-eqv ...) ;; 成功するはず
test-runner は test suite を実行し、その状態を管理するオブジェクトである。 test group path と skip specifier、expected fail specifier は test runner の一部である。 通常、test -runner は実行したテストの統計情報も保存している。
(test-runner? value)
value
が test runner
オブジェクトであるとき、かつそのときにかぎり真を返す。
(test-runner-current)
(test-runner-current runner)
現在の test runner を設定、または変更する。
実装系がパラメータオブジェクト
(SRFI-39),
をサポートする場合、test-runner-current
はパラメータオブジェクトであってもよい。
また、それ以外に、流動変数やスレッドローカルな変数、グローバル変数を
つかって、マクロや関数として実装してもよい。
(test-runner-get)
(test-runner-current)
同じであるが、
test runner が設定されていない場合には例外が送出される。
(test-runner-simple)
エラーとテスト結果の要約を標準出力ポートに出力するだけの 単純な test runner を新しく作成して返す。
(test-runner-null)
テスト結果にして何の処理も行わない test runner を新しく作成して返す。 これは主に特別な test runner を書くときの拡張用に用意されている。
実装系は (test-runner-gui)
のようなほかの
test runner を提供してもかまわない。
(test-runner-create)
test-runner を新しく作成して返す。
((test-runner-factory))
と同等である。
(test-runner-factory)
(test-runner-factory factory)
test runner factory を設定または変更する。
factory は新規の test runner を作成する零引き数の関数である。
デフォルト値は test-runner-simple
であるが、実装系はデフォルト値を変更する方法を用意してもかまわない。
test-runner-current
と同様に、パラメータオブジェクトであってもよいし、
スレッドローカル変数、流動変数、グローバル変数を使ってもかまわない。
(test-apply [runner] specifier ... procedure)
runner を現在の test runner に設定し、procedure
を引き数なしで呼び出す。
runner が省略された場合には (test-runner-current)
が使われる(test runner が設定されていない場合には test-begin
の場合と同様に新たに作成される)。
specifier がひとつ以上指定されていた場合には
specifier に一致したテストだけが実行される。
specifier は test-skip
と同様の形式で指定する。
最終的に specifier のすべてにマッチし、
かつ現在有効な test-skip
specifier
に一致しなかったテストだけが実行される。
(test-with-runner runner decl-or-expr ...)
runner を現在の test runner に設定して decl-or-expr を実行する。
テストを実行すると、test runner にさまざまなプロパティが設定される。 この値はあつらえの test runner や(稀ではあるが) test suite から利用することができる。
テストを実行すると、以下のシンボルが返る。
'pass
'fail
'xfail
'xpass
'skip
(test-result-kind [runner])
いちばん最近のテストについて、上記の結果コードのひとつを返す。
テストがまだひとつも実行されていない場合には #f
が返る。
テストを開始したがまだ結果が出ていない場合には、
テストが失敗すると予想されている場合には 'xfail
、
テストが無視される場合には 'xskip
、
それ以外は #f
が返る。
(test-passed? [runner])
(test-result-kind [runner])
の値が 'pass
、'xpass
のいずれかの場合に真を返す。
これは、直前のテストが成功した場合に特定のテストを実行するような
test suite に便利なように用意された略記法である。
test runner はまた、現在の、もしくはもっとも最近のテストについて、より詳細な result property を保持している(もっとも最近のテストの result property は次のテストが開始されるまで参照可能である)。 それぞれの property には名前(シンボル)と値(任意のオブジェクト)がある。 また、標準の property や実装系により設定される property もある。 実装系はここにさらに追加してもかまわない。
(test-result-ref runner 'pname [default])
pname という名前のプロパティの値を返す。
そのような値がない場合には default を返す。
default が指定されていない場合は #f
を返す。
(test-result-set! runner 'pname value)
pname という名前のプロパティの値を value に設定する。 この関数を呼び出すのはたいてい実装系であるが、あつらえの test runner が属性を追加するのにも使うことができる。
(test-result-remove runner 'pname)
'pname
という名前の属性を取り除く。
(test-result-clear runner)
result プロパティをすべて取り除く。
実装系は test-assert
等の呼び出しの最初に、
自動的に test-result-clear
を呼び出す。
(test-result-alist runner)
result property を連想リストとして返す。
戻り値が test runner と状態を共有しているかどうかは未定義である。
また、戻り値は直接変更すべきではないが、
値を取り出したあとに test-result-set!
や
test-result-remove
の呼び出すと、間接的に変更されるかもしれない。
しかし、test-result-clear
はこの戻り値の連想リストを変更しない。
したがって、これにより前回のテストの実行結果のオブジェクトを保存しておく
ことができる。
使用可能な result property は実装系依存である。 しかし、以下のものが提供されるように提案されている。
'result-kind
(test-result-kind runner)
は
(test-result-ref runner 'result-kind)
と等価である。
'source-file
'source-line
test-assert
等)の test suite
のソースコード中での位置(可能ならば)'source-form
'expected-value
'expected-error
test-error
で指定される error-type
(可能、かつ有意義な場合)'actual-value
'actual-error
この節では test runner の記述法について述べる。 test case を書きたいだけの場合には読みとばしてかまわない。
これらのコールバック関数は(オブジェクト指向で言うところの)
test runner のメソッドである。
test-runner-on-event
メソッドは event が発生したときに実装系により呼び出される。
event に対するコールバック関数の定義(設定)は次の式で行う (これはふつう test runner の初期化時におこなわれる)。
(test-runner-on-event! runner event-function)
event-function は引き数として test runner を取り、 さらにそれに加えて event 依存の引き数をとることがある。
evennt に対するコールバック関数の抽出(取り出し)は次のようにおこなう。
(test-runner-on-event runner)
event に対するコールバック関数を抽出し呼び出すには次のようにする (これはふつう実装のコアでつかわれる)。
((test-runner-on-event runner) runner other-args ...)
以下のようなフック関数が利用可能である。
(test-runner-on-test-begin runner)
(test-runner-on-test-begin! runner on-test-begin-function)
(on-test-begin-function runner)
on-test-begin-function は個別の test case の最初、対象の式(と期待される値)が評価される前に呼び出される。
(test-runner-on-test-end runner)
(test-runner-on-test-end! runner on-test-end-function)
(on-test-end-function runner)
on-test-end-function は test case の最後、テストの結果が参照可能になったところで呼び出される。
(test-runner-on-group-begin runner)
(test-runner-on-group-begin! runner on-group-begin-function)
(on-group-begin-function runner suite-name count)
on-group-begin-function は test-group
の最初にある test-begin
で呼び出される。
suite-name は文字列であり、count は整数値ないし
#f
である。
(test-runner-on-group-end runner)
(test-runner-on-group-end! runner on-group-end-function)
(on-group-end-function runner)
on-group-end-function は test-group
の末尾にある test-end
で呼び出される。
(test-runner-on-bad-count runner)
(test-runner-on-bad-count! runner on-bad-count-function)
(on-bad-count-function runner actual-count expected-count)
test-end
の時点で
test-begin
で指定した expected-count
が実際に実行ないし無視されたテストの個数 actual-count
と一致しない場合に(on-group-end-function
が呼ばれる前に)呼び出される。
(test-runner-on-base-end-name runner)
(test-runner-on-bad-end-name! runner on-bad-end-name-function)
(on-bad-end-name-function runner begin-name end-name)
test-end
で suite-name
が指定され、それが test-begin
で指定されたものと一致しないときに
(on-group-end-function が呼び出される前に)呼び出される。
(test-runner-on-final runner)
(test-runner-on-final! runner on-final-function)
(on-final-function runner)
引き数として test runner を取り、典型的にはテストの個数や概要を表示する。
いちばん外側の test-end
に対応する
on-group-end-function が呼び出されたあとに呼ばれる。
デフォルト値は、test-on-final-simple
で、標準出力ポートにさまざまなテストの個数を表示する。
test-runner-simple
の返すデフォルトの test runner
は以下のコールバック関数をつかう。
(test-on-test-begin-simple runner)
(test-on-test-end-simple runner)
(test-on-group-begin-simple runner suite-name count)
(test-on-group-end-simple runner)
(test-on-bad-count-simple runner actual-count expected-count)
(test-on-bad-end-name-simple runner begin-name end-name)
これらは自分で test runner を書くときに呼び出すことができる。
以下の関数は test runner のその他の構成要素を利用するための関数である。 これらが必要になるのは、test runner やマッチ述語を新規に書く場合だけであろう。
(test-runner-pass-count runner)
成功すると予想されてい実際に成功したテストの個数を返す。
(test-runner-fail-count runner)
成功すると予想されていたが実際には失敗したテストの個数を返す。
(test-runner-xpass-count runner)
失敗すると予想されていたが実際には成功したテストの個数を返す。
(test-runner-xfail-count runner)
失敗すると予想されてい実際に失敗したテストの個数を返す。
(test-runner-skip-count runner)
無視された test および test group の個数を返す。
(test-runner-test-name runner)
現在の test ないし test case の名前を文字列として返す。
test-begin
の実行中には test group の名前を返し、
実際のテストの実行中には test case の名前を返す。
名前が指定されていなかった場合には空文字列を返す。
(test-runner-group-path runner)
現在テスト中の test group の名前を外側から順にリストにしたものを返す。
(test-runner-group-stack runner)
現在テスト中の test group の名前を内側から順にリストにしたものを返す
(オブジェクトのコピーの必要がないため test-runner-group-path
よりも効率がよい)。
(test-runner-aux-value runner)
(test-runner-aux-value! runner on-test)
test runner の aux-value
フィールドの値を設定ないし取得する。
このフィールドは本仕様の API や test-runner-simple
の返す test runner では使用されないが、あつらえの test runner
を書く場合に状態変数を追加するのに使うことができる。
(test-runner-reset runner)
runner の状態を初期状態に戻す。
これはシンプルなあつらえの test runner の例である。 test suite を実行する前にこのプログラムを読み込んでおくと、 この test runner をデフォルトの test runner に設定する。
(define (my-simple-runner filename) (let ((runner (test-runner-null)) (port (open-output-file filename)) (num-passed 0) (num-failed 0)) (test-runner-on-test! runner (lambda (runner result) (case (cdr (assq 'result-kind result)) ((pass xpass) (set! num-passed (+ num-passed 1))) ((fail xfail) (set! num-failed (+ num-failed 1))) (else #t)))) (test-runner-on-final! runner (lambda (runner) (format port "Passing tests: ~d.~%Failing tests: ~d.~%" num-passed num-failed) (close-output-port port))) runner)) (test-runner-factory (lambda () (my-simple-runner "/tmp/my-test.log")))
テスト実装では特定の SRFI
(srfi-9
, srfi-34
, srfi-35
,
srfi-39
)や実装系(Kawa)に依存する部分を切り換えるのに
cond-expand
(SRFI-0)
を使用した。
それ以外はすべての R5RS 実装系についてポータブルである。
Jussi Piitulainen の
SRFI-25 のテスト
test.scm
を変換した srfi-25-test.scm
がある。
もちろん、testing framework 自体の test suite も必要である。
この srfi-64-test.scm
は Donovan Kolbly
<donovan@rscheme.org>
によるものである。
Copyright (C) Per Bothner (2005, 2006)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Last modified: Tue Feb 21 16:20:11 PST 2006