Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add downcast test api to help testrunners #200

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/Elm/Kernel/Test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import Elm.Kernel.Utils exposing (Tuple0)
import Result exposing (Err, Ok)
import Maybe exposing (Just, Nothing)

*/

Expand All @@ -16,3 +17,17 @@ function _Test_runThunk(thunk)
return __Result_Err(err.toString());
}
}

var _Test_elmTestSymbol = Symbol("elmTestSymbol");

function _Test_tagTest(test)
{
test[_Test_elmTestSymbol] = true;
return test;
}

function _Test_downcastTest(value)
{
return value && value[_Test_elmTestSymbol] ? $elm$core$Maybe$Just(value) : $elm$core$Maybe$Nothing;
}

77 changes: 49 additions & 28 deletions src/Test.elm
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ concat tests =
{ description = "This `concat` has no tests in it. Let's give it some!"
, reason = Invalid EmptyList
}
|> Internal.wrapTest

else
case Internal.duplicatedName tests of
Expand All @@ -66,9 +67,13 @@ concat tests =
{ description = String.join "\n" (List.map dupDescription <| Set.toList dups)
, reason = Invalid DuplicatedName
}
|> Internal.wrapTest

Ok _ ->
Internal.ElmTestVariant__Batch tests
tests
|> List.map (Internal.unwrapTest)
|> Internal.Batch
|> Internal.wrapTest


{-| Apply a description to a list of tests.
Expand Down Expand Up @@ -106,12 +111,14 @@ describe untrimmedDesc tests =
{ description = "This `describe` has a blank description. Let's give it a useful one!"
, reason = Invalid BadDescription
}
|> Internal.wrapTest

else if List.isEmpty tests then
Internal.failNow
{ description = "This `describe " ++ desc ++ "` has no tests in it. Let's give it some!"
, reason = Invalid EmptyList
}
|> Internal.wrapTest

else
case Internal.duplicatedName tests of
Expand All @@ -120,22 +127,28 @@ describe untrimmedDesc tests =
dupDescription duped =
"Contains multiple tests named '" ++ duped ++ "'. Let's rename them so we know which is which."
in
Internal.ElmTestVariant__Labeled desc <|
Internal.failNow
{ description = String.join "\n" (List.map dupDescription <| Set.toList dups)
, reason = Invalid DuplicatedName
}
Internal.failNow
{ description = String.join "\n" (List.map dupDescription <| Set.toList dups)
, reason = Invalid DuplicatedName
}
|> Internal.Labeled desc
|> Internal.wrapTest

Ok childrenNames ->
if Set.member desc childrenNames then
Internal.ElmTestVariant__Labeled desc <|
Internal.failNow
{ description = "The test '" ++ desc ++ "' contains a child test of the same name. Let's rename them so we know which is which."
, reason = Invalid DuplicatedName
}
Internal.failNow
{ description = "The test '" ++ desc ++ "' contains a child test of the same name. Let's rename them so we know which is which."
, reason = Invalid DuplicatedName
}
|> Internal.Labeled desc
|> Internal.wrapTest

else
Internal.ElmTestVariant__Labeled desc (Internal.ElmTestVariant__Batch tests)
tests
|> List.map (Internal.unwrapTest)
|> Internal.Batch
|> Internal.Labeled desc
|> Internal.wrapTest


{-| Return a [`Test`](#Test) that evaluates a single
Expand All @@ -159,9 +172,12 @@ test untrimmedDesc thunk =
in
if String.isEmpty desc then
Internal.blankDescriptionFailure
|> Internal.wrapTest

else
Internal.ElmTestVariant__Labeled desc (Internal.ElmTestVariant__UnitTest (\() -> [ thunk () ]))
Internal.UnitTest (\() -> [ thunk () ])
|> Internal.Labeled desc
|> Internal.wrapTest


{-| Returns a [`Test`](#Test) that is "TODO" (not yet implemented). These tests
Expand Down Expand Up @@ -190,6 +206,7 @@ todo desc =
{ description = desc
, reason = TODO
}
|> Internal.wrapTest


{-| Returns a [`Test`](#Test) that causes other tests to be skipped, and
Expand Down Expand Up @@ -230,7 +247,7 @@ an `only` inside a `skip`, it will also get skipped.
-}
only : Test -> Test
only =
Internal.ElmTestVariant__Only
Internal.unwrapTest >> Internal.Only >> Internal.wrapTest


{-| Returns a [`Test`](#Test) that gets skipped.
Expand Down Expand Up @@ -266,7 +283,7 @@ an `only` inside a `skip`, it will also get skipped.
-}
skip : Test -> Test
skip =
Internal.ElmTestVariant__Skipped
Internal.unwrapTest >> Internal.Skipped >> Internal.wrapTest


{-| Options [`fuzzWith`](#fuzzWith) accepts.
Expand Down Expand Up @@ -350,37 +367,41 @@ fuzzWith options fuzzer desc getTest =
{ description = "Fuzz tests must have a run count of at least 1, not " ++ String.fromInt options.runs ++ "."
, reason = Invalid NonpositiveFuzzCount
}
|> Internal.wrapTest

else
fuzzWithHelp options (Test.Fuzz.fuzzTest options.distribution fuzzer desc getTest)
fuzzWithHelp
options
(Test.Fuzz.fuzzTest options.distribution fuzzer desc getTest |> Internal.unwrapTest)
|> Internal.wrapTest


fuzzWithHelp : FuzzOptions a -> Test -> Test
fuzzWithHelp : FuzzOptions a -> Internal.TestData -> Internal.TestData
fuzzWithHelp options aTest =
case aTest of
Internal.ElmTestVariant__UnitTest _ ->
Internal.UnitTest _ ->
aTest

Internal.ElmTestVariant__FuzzTest run ->
Internal.ElmTestVariant__FuzzTest (\seed _ -> run seed options.runs)
Internal.FuzzTest run ->
Internal.FuzzTest (\seed _ -> run seed options.runs)

Internal.ElmTestVariant__Labeled label subTest ->
Internal.ElmTestVariant__Labeled label (fuzzWithHelp options subTest)
Internal.Labeled label subTest ->
Internal.Labeled label (fuzzWithHelp options subTest)

Internal.ElmTestVariant__Skipped subTest ->
Internal.Skipped subTest ->
-- It's important to treat skipped tests exactly the same as normal,
-- until after seed distribution has completed.
fuzzWithHelp options subTest
|> Internal.ElmTestVariant__Only
|> Internal.Only

Internal.ElmTestVariant__Only subTest ->
Internal.Only subTest ->
fuzzWithHelp options subTest
|> Internal.ElmTestVariant__Only
|> Internal.Only

Internal.ElmTestVariant__Batch tests ->
Internal.Batch tests ->
tests
|> List.map (fuzzWithHelp options)
|> Internal.ElmTestVariant__Batch
|> Internal.Batch


{-| Take a function that produces a test, and calls it several (usually 100) times, using a randomly-generated input
Expand Down
12 changes: 7 additions & 5 deletions src/Test/Fuzz.elm
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Simplify
import Test.Distribution exposing (DistributionReport(..))
import Test.Distribution.Internal exposing (Distribution(..), ExpectedDistribution(..))
import Test.Expectation exposing (Expectation(..))
import Test.Internal exposing (Test(..), blankDescriptionFailure)
import Test.Internal exposing (Test(..), TestData(..), blankDescriptionFailure)
import Test.Runner.Distribution
import Test.Runner.Failure exposing (InvalidReason(..), Reason(..))

Expand All @@ -27,16 +27,18 @@ fuzzTest distribution fuzzer untrimmedDesc getExpectation =
in
if String.isEmpty desc then
blankDescriptionFailure
|> Test.Internal.wrapTest

else
ElmTestVariant__Labeled desc <| validatedFuzzTest fuzzer getExpectation distribution

validatedFuzzTest fuzzer getExpectation distribution
|> Labeled desc
|> Test.Internal.wrapTest

{-| Knowing that the fuzz test isn't obviously invalid, run the test and package up the results.
-}
validatedFuzzTest : Fuzzer a -> (a -> Expectation) -> Distribution a -> Test
validatedFuzzTest : Fuzzer a -> (a -> Expectation) -> Distribution a -> TestData
validatedFuzzTest fuzzer getExpectation distribution =
ElmTestVariant__FuzzTest
FuzzTest
(\seed runs ->
let
runResult : RunResult
Expand Down
60 changes: 38 additions & 22 deletions src/Test/Internal.elm
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
module Test.Internal exposing (Test(..), blankDescriptionFailure, duplicatedName, failNow, toString)
module Test.Internal exposing (Test, TestData(..), blankDescriptionFailure, duplicatedName, failNow, toString, wrapTest, unwrapTest)

import Random
import Set exposing (Set)
import Test.Expectation exposing (Expectation)
import Test.Runner.Failure exposing (InvalidReason(..), Reason(..))
import Elm.Kernel.Test


{-| All variants of this type has the `ElmTestVariant__` prefix so that
node-test-runner can recognize them in the compiled JavaScript. This lets us
add more variants here without having to update the runner.
type TestData
= UnitTest (() -> List Expectation)
| FuzzTest (Random.Seed -> Int -> List Expectation)
| Labeled String TestData
| Skipped TestData
| Only TestData
| Batch (List TestData)

For more information, see <https://github.com/elm-explorations/test/pull/153>

{-| Newtype wrapper around TestData so that
node-test-runner can recognize them in the compiled JavaScript.

**MUST** only be constructed by the kernel
-}
type Test
= ElmTestVariant__UnitTest (() -> List Expectation)
| ElmTestVariant__FuzzTest (Random.Seed -> Int -> List Expectation)
| ElmTestVariant__Labeled String Test
| ElmTestVariant__Skipped Test
| ElmTestVariant__Only Test
| ElmTestVariant__Batch (List Test)
= Wrapped TestData


{-| Create a test that always fails for the given reason and description.
-}
failNow : { description : String, reason : Reason } -> Test
failNow : { description : String, reason : Reason } -> TestData
failNow record =
ElmTestVariant__UnitTest
UnitTest
(\() -> [ Test.Expectation.fail record ])


blankDescriptionFailure : Test
blankDescriptionFailure : TestData
blankDescriptionFailure =
failNow
{ description = "This test has a blank description. Let's give it a useful one!"
Expand All @@ -41,25 +44,25 @@ blankDescriptionFailure =
duplicatedName : List Test -> Result (Set String) (Set String)
duplicatedName tests =
let
names : Test -> List String
names : TestData -> List String
names test =
case test of
ElmTestVariant__Labeled str _ ->
Labeled str _ ->
[ str ]

ElmTestVariant__Batch subtests ->
Batch subtests ->
List.concatMap names subtests

ElmTestVariant__UnitTest _ ->
UnitTest _ ->
[]

ElmTestVariant__FuzzTest _ ->
FuzzTest _ ->
[]

ElmTestVariant__Skipped subTest ->
Skipped subTest ->
names subTest

ElmTestVariant__Only subTest ->
Only subTest ->
names subTest

accumDuplicates : String -> ( Set String, Set String ) -> ( Set String, Set String )
Expand All @@ -71,7 +74,9 @@ duplicatedName tests =
( dups, Set.insert newName uniques )

( dupsAccum, uniquesAccum ) =
List.concatMap names tests
tests
|> List.map (\(Wrapped td) -> td)
|> List.concatMap names
|> List.foldl accumDuplicates ( Set.empty, Set.empty )
in
if Set.isEmpty dupsAccum then
Expand All @@ -84,3 +89,14 @@ duplicatedName tests =
toString : a -> String
toString =
Elm.Kernel.Debug.toString


wrapTest : TestData -> Test
wrapTest td =
Elm.Kernel.Test.tagTest (Wrapped td)


unwrapTest : Test -> TestData
unwrapTest (Wrapped t) =
t

Loading