Skip to content

Commit c1c2a34

Browse files
authored
Fix tests to pass with -f-with-http (#2382)
… partly by mocking some imports better and partly by disabling some tests that are not worth the effort to mock. Fixes #2380
1 parent dcebd92 commit c1c2a34

File tree

6 files changed

+128
-58
lines changed

6 files changed

+128
-58
lines changed

dhall/dhall.cabal

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ Flag cross
196196
Default: False
197197
Manual: True
198198

199+
Flag network-tests
200+
Description: Enable tests which depend on an internet connection
201+
Default: True
202+
Manual: True
203+
199204
Common common
200205
Build-Depends:
201206
base >= 4.11.0.0 && < 5 ,
@@ -253,6 +258,9 @@ Common common
253258
if flag(use-http-client-tls)
254259
CPP-Options:
255260
-DUSE_HTTP_CLIENT_TLS
261+
if flag(network-tests)
262+
CPP-Options:
263+
-DNETWORK_TESTS
256264

257265
GHC-Options: -Wall -Wcompat -Wincomplete-uni-patterns
258266

dhall/src/Dhall/Tutorial.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
2+
{-# LANGUAGE CPP #-}
23

34
{-| Dhall is a programming language specialized for configuration files. This
45
module contains a tutorial explaining how to author configuration files
@@ -375,13 +376,23 @@ import Dhall
375376
--
376377
-- ... and you can reference that expression either directly:
377378
--
379+
#if defined(WITH_HTTP) && defined(USE_HTTP_CLIENT_TLS) && defined(NETWORK_TESTS)
378380
-- >>> input auto "https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool
379381
-- True
382+
#else
383+
-- > >>> input auto "https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool
384+
-- > True
385+
#endif
380386
--
381387
-- ... or inside of a larger expression:
382388
--
389+
#if defined(WITH_HTTP) && defined(USE_HTTP_CLIENT_TLS) && defined(NETWORK_TESTS)
383390
-- >>> input auto "False == https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool
384391
-- False
392+
#else
393+
-- > >>> input auto "False == https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool
394+
-- > False
395+
#endif
385396
--
386397
-- You're not limited to hosting Dhall expressions on GitHub. You can host a
387398
-- Dhall expression anywhere that you can host UTF8-encoded text on the web,

dhall/tests/Dhall/Test/Import.hs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE CPP #-}
12
{-# LANGUAGE OverloadedStrings #-}
23
{-# LANGUAGE TypeApplications #-}
34

@@ -21,14 +22,18 @@ import qualified Dhall.Core as Core
2122
import qualified Dhall.Import as Import
2223
import qualified Dhall.Parser as Parser
2324
import qualified Dhall.Test.Util as Test.Util
24-
import qualified Network.HTTP.Client as HTTP
25-
import qualified Network.HTTP.Client.TLS as HTTP
2625
import qualified System.FilePath as FilePath
2726
import qualified System.IO.Temp as Temp
2827
import qualified Test.Tasty as Tasty
2928
import qualified Test.Tasty.HUnit as Tasty.HUnit
3029
import qualified Turtle
3130

31+
#if defined(WITH_HTTP) && defined(NETWORK_TESTS)
32+
import qualified Network.HTTP.Client as HTTP
33+
import qualified Network.HTTP.Client.TLS as HTTP
34+
#endif
35+
36+
3237
importDirectory :: FilePath
3338
importDirectory = "./dhall-lang/tests/import"
3439

@@ -62,6 +67,18 @@ getTests = do
6267
let expectedSuccesses =
6368
[ importDirectory </> "failure/unit/DontRecoverCycle.dhall"
6469
, importDirectory </> "failure/unit/DontRecoverTypeError.dhall"
70+
#if !(defined(WITH_HTTP) && defined(NETWORK_TESTS))
71+
-- We attempt to simulate test.dhall-lang.org, but even so
72+
-- some tests unexpectedly succeed due to the inadequacy of
73+
-- the simulation
74+
, importDirectory </> "failure/unit/cors/OnlySelf.dhall"
75+
, importDirectory </> "failure/unit/cors/OnlyOther.dhall"
76+
, importDirectory </> "failure/unit/cors/Null.dhall"
77+
, importDirectory </> "failure/unit/cors/TwoHops.dhall"
78+
, importDirectory </> "failure/unit/cors/Empty.dhall"
79+
, importDirectory </> "failure/unit/cors/NoCORS.dhall"
80+
, importDirectory </> "failure/originHeadersFromRemote.dhall"
81+
#endif
6582
]
6683

6784
_ <- Monad.guard (path `notElem` expectedSuccesses)
@@ -84,7 +101,15 @@ successTest prefix = do
84101

85102
let directoryString = FilePath.takeDirectory inputPath
86103

87-
let expectedFailures = [ ]
104+
let expectedFailures =
105+
[
106+
#if !(defined(WITH_HTTP) && defined(NETWORK_TESTS))
107+
importDirectory </> "success/originHeadersImportFromEnv"
108+
, importDirectory </> "success/originHeadersImport"
109+
, importDirectory </> "success/originHeadersOverride"
110+
, importDirectory </> "success/unit/asLocation/RemoteChainEnv"
111+
#endif
112+
]
88113

89114
Test.Util.testCase prefix expectedFailures (do
90115

@@ -98,6 +123,7 @@ successTest prefix = do
98123

99124
let originalCache = "dhall-lang/tests/import/cache"
100125

126+
#if defined(WITH_HTTP) && defined(NETWORK_TESTS)
101127
let httpManager =
102128
HTTP.newManager
103129
HTTP.tlsManagerSettings
@@ -108,6 +134,9 @@ successTest prefix = do
108134
httpManager
109135
(pure Import.envOriginHeaders)
110136
directoryString
137+
#else
138+
let status = Import.emptyStatus directoryString
139+
#endif
111140

112141
let load =
113142
State.evalStateT

dhall/tests/Dhall/Test/TypeInference.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ successTest :: Text -> TestTree
5959
successTest prefix = do
6060
let expectedFailures =
6161
[]
62-
#ifdef WITH_HTTP
62+
#if defined(WITH_HTTP) && defined(NETWORK_TESTS)
6363
#else
6464
++ [ typeInferenceDirectory </> "success/CacheImports"
6565
]

dhall/tests/Dhall/Test/Util.hs

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE NamedFieldPuns #-}
23
{-# LANGUAGE OverloadedStrings #-}
34
{-# LANGUAGE RankNTypes #-}
45
{-# LANGUAGE ViewPatterns #-}
@@ -49,7 +50,6 @@ import Turtle (FilePath, Pattern, Shell, fp)
4950
import qualified Control.Exception
5051
import qualified Control.Foldl as Foldl
5152
import qualified Control.Monad.Trans.State.Strict as State
52-
import qualified Data.Foldable
5353
import qualified Data.Functor
5454
import qualified Data.Text as Text
5555
import qualified Data.Text.IO as Text.IO
@@ -64,9 +64,11 @@ import qualified Test.Tasty as Tasty
6464
import qualified Test.Tasty.ExpectedFailure as Tasty.ExpectedFailure
6565
import qualified Turtle
6666

67-
#ifndef WITH_HTTP
67+
#if defined(WITH_HTTP) && defined(NETWORK_TESTS)
68+
import qualified Data.Foldable
69+
#else
6870
import Control.Monad.IO.Class (MonadIO (..))
69-
import Dhall.Core (URL (..))
71+
import Dhall.Core (URL (..), File (..), Directory (..))
7072
import Lens.Family.State.Strict (zoom)
7173

7274
import qualified Data.Foldable
@@ -104,62 +106,85 @@ loadRelativeTo rootDirectory semanticCacheMode expression =
104106
(loadWith expression)
105107
(Dhall.Import.emptyStatus rootDirectory) { _semanticCacheMode = semanticCacheMode }
106108

107-
#ifdef WITH_HTTP
109+
#if defined(WITH_HTTP) && defined(NETWORK_TESTS)
108110
loadWith :: Expr Src Import -> StateT Status IO (Expr Src Void)
109111
loadWith = Dhall.Import.loadWith
110112

111113
#else
112114
loadWith :: Expr Src Import -> StateT Status IO (Expr Src Void)
113115
loadWith expr = do
114-
let mockRemote' url = do
115-
liftIO . putStrLn $ "\nTesting without real HTTP support --"
116-
++ " using mock HTTP client to resolve remote import."
117-
mockRemote url
118-
zoom Dhall.Import.remote (State.put mockRemote')
116+
zoom Dhall.Import.remote (State.put mockRemote)
119117
Dhall.Import.loadWith expr
120118

121119
mockRemote :: Dhall.Core.URL -> StateT Status IO Data.Text.Text
122-
-- Matches anything pointing to
123-
-- `https://raw.githubusercontent.com/dhall-lang/dhall-lang/master/`
124-
mockRemote (URL { authority = "raw.githubusercontent.com"
125-
, path = Dhall.Core.File (Dhall.Core.Directory components) file })
126-
| take 3 (reverse components) == ["dhall-lang", "dhall-lang", "master"] = do
127-
let dropEnd n ls = take (length ls - n) ls
128-
let localDir = dropEnd 3 components ++ ["dhall-lang"]
129-
130-
localPath <- Dhall.Import.localToPath Dhall.Core.Here (Dhall.Core.File (Dhall.Core.Directory localDir) file)
131-
liftIO $ Data.Text.IO.readFile localPath
132-
133-
-- Matches anything pointing to
134-
-- `https://test.dhall-lang.org/Bool/package.dhall`; checks that a `test` header
135-
-- is present and redirects to the local copy of the prelude.
136-
mockRemote (URL { authority = "test.dhall-lang.org"
137-
, path = Dhall.Core.File (Dhall.Core.Directory components) file
138-
, headers = Just headersExpr }) =
139-
case Data.Foldable.find ((== "test") . fst) hs of
140-
Nothing -> fail $ "(mock http) Tried to load an import from "
141-
++"\"test.dhall-lang.org\""
142-
++ "without setting the \"test\" header field."
143-
Just (_, _) -> do
144-
let localDir = components ++ ["Prelude", "dhall-lang"]
145-
localPath <- Dhall.Import.localToPath Dhall.Core.Here (Dhall.Core.File (Dhall.Core.Directory localDir) file)
146-
liftIO $ Data.Text.IO.readFile localPath
147-
where
148-
hs = Dhall.Import.toHeaders headersExpr
149-
150-
-- Emulates `https://httpbin.org/user-agent`
151-
mockRemote (URL { authority = "httpbin.org"
152-
, path = Dhall.Core.File (Dhall.Core.Directory []) "user-agent"
153-
, headers = Just headersExpr }) =
154-
case Data.Foldable.find ((== "user-agent") . fst) hs of
155-
Nothing -> fail $ "(mock http) Tried to read the user agent via "
156-
++ "\"httpbin.com/user-agent\" without supplying one "
157-
++ "in the header!"
158-
Just (_, userAgent) -> do
120+
mockRemote
121+
url@URL
122+
{ authority = "raw.githubusercontent.com"
123+
, path = File (Directory components) file
124+
} = do
125+
let localDir = case reverse components of
126+
"dhall-lang" : "dhall-lang" : _ : rest ->
127+
reverse ("dhall-lang" : rest)
128+
"Nadrieril" : "dhall-rust" : _ : "dhall" : rest ->
129+
reverse ("dhall-lang" : rest)
130+
_ -> do
131+
fail ("Unable to mock URL: " <> Text.unpack (Dhall.Core.pretty url))
132+
133+
localPath <- Dhall.Import.localToPath Dhall.Core.Here (File (Directory localDir) file)
134+
135+
liftIO (Data.Text.IO.readFile localPath)
136+
137+
mockRemote
138+
URL { authority = "prelude.dhall-lang.org"
139+
, path = File (Directory components) file
140+
} = do
141+
let localDir = components ++ [ "Prelude", "dhall-lang" ]
142+
143+
localPath <- Dhall.Import.localToPath Dhall.Core.Here (File (Directory localDir) file)
144+
145+
liftIO (Data.Text.IO.readFile localPath)
146+
147+
mockRemote url@URL{ authority = "test.dhall-lang.org", path, headers } =
148+
case (path, fmap Dhall.Import.toHeaders headers) of
149+
(File (Directory []) "foo", Just [("test", _)]) ->
150+
return "./bar"
151+
(File (Directory []) "bar", Just [("test", _)]) ->
152+
return "True"
153+
(File (Directory ["cors"]) "AllowedAll.dhall", _) ->
154+
return "42"
155+
(File (Directory ["cors"]) "OnlyGithub.dhall", _) ->
156+
return "42"
157+
(File (Directory ["cors"]) "OnlySelf.dhall", _) ->
158+
return "42"
159+
(File (Directory ["cors"]) "OnlyOther.dhall", _) ->
160+
return "42"
161+
(File (Directory ["cors"]) "Empty.dhall", _) ->
162+
return "42"
163+
(File (Directory ["cors"]) "NoCORS.dhall", _) ->
164+
return "42"
165+
(File (Directory ["cors"]) "Null.dhall", _) ->
166+
return "42"
167+
(File (Directory ["cors"]) "SelfImportAbsolute.dhall", _) ->
168+
return "https://test.dhall-lang.org/cors/NoCORS.dhall"
169+
(File (Directory ["cors"]) "SelfImportRelative.dhall", _) ->
170+
return "./NoCORS.dhall"
171+
(File (Directory ["cors"]) "TwoHopsFail.dhall", _) ->
172+
return "https://raw.githubusercontent.com/dhall-lang/dhall-lang/5ff7ecd2411894dd9ce307dc23020987361d2d43/tests/import/data/cors/OnlySelf.dhall"
173+
(File (Directory ["cors"]) "TwoHopsSuccess.dhall", _) ->
174+
return "https://raw.githubusercontent.com/dhall-lang/dhall-lang/5ff7ecd2411894dd9ce307dc23020987361d2d43/tests/import/data/cors/OnlyGithub.dhall"
175+
_ -> do
176+
fail ("Unable to mock URL: " <> Text.unpack (Dhall.Core.pretty url))
177+
178+
mockRemote url@URL{ authority = "httpbin.org", path, headers } =
179+
case (path, fmap Dhall.Import.toHeaders headers) of
180+
(File (Directory []) "user-agent", Just [("user-agent", userAgent)]) -> do
159181
let agentText = Data.Text.Encoding.decodeUtf8 userAgent
182+
160183
return ("{\n \"user-agent\": \"" <> agentText <> "\"\n}\n")
161-
where
162-
hs = Dhall.Import.toHeaders headersExpr
184+
(File (Directory []) "user-agent", Nothing) -> do
185+
return ("{\n \"user-agent\": \"Dhall\"\n}\n")
186+
_ -> do
187+
fail ("Unable to mock URL: " <> Text.unpack (Dhall.Core.pretty url))
163188

164189
mockRemote url = do
165190
let urlString = Text.unpack (Dhall.Core.pretty url)

nix/shared.nix

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,9 @@ let
150150
).overrideAttrs (old: { XDG_CACHE_HOME=".cache"; });
151151

152152
dhall-no-http =
153-
# The import tests fail with HTTP support compiled out
154-
pkgsNew.haskell.lib.dontCheck
155-
(pkgsNew.haskell.lib.appendConfigureFlag
156-
haskellPackagesNew.dhall
157-
[ "-f-with-http" ]
158-
);
153+
pkgsNew.haskell.lib.appendConfigureFlag
154+
haskellPackagesNew.dhall
155+
[ "-f-with-http" ];
159156

160157
dhall-bash =
161158
haskellPackagesNew.callCabal2nix

0 commit comments

Comments
 (0)