Skip to content

Commit 91a4b8c

Browse files
committed
Merge pull request #340 from commercialhaskell/339-integration-tests
339 integration tests
2 parents 591a172 + 544994b commit 91a4b8c

File tree

12 files changed

+277
-63
lines changed

12 files changed

+277
-63
lines changed

.travis.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ before_install:
2222
- travis_retry sudo add-apt-repository -y ppa:hvr/ghc
2323
- travis_retry sudo apt-get update
2424
- travis_retry sudo apt-get install cabal-install-$CABALVER ghc-$GHCVER # see note about happy/alex
25-
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
25+
- export PATH=$HOME/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
2626

2727
install:
2828
- cabal --version
@@ -39,6 +39,12 @@ script:
3939
- cabal check
4040
- cabal sdist # tests that a source-distribution can be generated
4141

42+
- cabal copy
43+
- cd test/integration
44+
- stack setup
45+
- stack test
46+
- cd ../..
47+
4248
# Check that the resulting source distribution can be built & installed.
4349
# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
4450
# `cabal install --force-reinstalls dist/*-*.tar.gz`

src/test/IntegrationTests.hs

Lines changed: 0 additions & 42 deletions
This file was deleted.

stack.cabal

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@ homepage: https://github.com/commercialhaskell/stack
2222
test/package-dump/ghc-7.8.txt
2323
test/package-dump/ghc-7.10.txt
2424

25-
flag integration-tests
26-
manual: True
27-
default: False
28-
description: Build the integration test executable
29-
3025
library
3126
hs-source-dirs: src/
3227
ghc-options: -Wall
@@ -177,21 +172,6 @@ executable stack
177172
, http-client
178173
default-language: Haskell2010
179174

180-
executable stack-integration-tests
181-
hs-source-dirs: src/test
182-
main-is: IntegrationTests.hs
183-
ghc-options: -Wall
184-
build-depends: base >= 4.7 && <5
185-
, hspec
186-
, process
187-
, temporary
188-
, directory
189-
default-language: Haskell2010
190-
if flag(integration-tests)
191-
buildable: True
192-
else
193-
buildable: False
194-
195175
test-suite stack-test
196176
type: exitcode-stdio-1.0
197177
hs-source-dirs: src/test

test/integration/Dummy.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
main :: IO ()
2+
main = return ()

test/integration/LICENSE

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Copyright (c) 2015, stack
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above copyright
9+
notice, this list of conditions and the following disclaimer in the
10+
documentation and/or other materials provided with the distribution.
11+
* Neither the name of stackage-common nor the
12+
names of its contributors may be used to endorse or promote products
13+
derived from this software without specific prior written permission.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

test/integration/Spec.hs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
{-# LANGUAGE DeriveDataTypeable #-}
2+
{-# LANGUAGE ScopedTypeVariables #-}
3+
4+
import Control.Applicative
5+
import Control.Arrow
6+
import Control.Concurrent.Async
7+
import Control.Exception
8+
import Control.Monad
9+
import Control.Monad.IO.Class
10+
import Control.Monad.Trans.Resource
11+
import qualified Data.ByteString.Lazy as L
12+
import Data.Char
13+
import Data.Conduit
14+
import Data.Conduit.Binary (sinkLbs)
15+
import Data.Conduit.Filesystem (sourceDirectoryDeep)
16+
import qualified Data.Conduit.List as CL
17+
import Data.Conduit.Process
18+
import Data.List (isSuffixOf, stripPrefix)
19+
import qualified Data.Map as Map
20+
import Data.Text.Encoding.Error (lenientDecode)
21+
import qualified Data.Text.Lazy as TL
22+
import qualified Data.Text.Lazy.Encoding as TL
23+
import Data.Typeable
24+
import System.Directory
25+
import System.Environment
26+
import System.Exit
27+
import System.FilePath
28+
import System.IO.Temp
29+
import System.PosixCompat.Files
30+
import Test.Hspec
31+
32+
main :: IO ()
33+
main = do
34+
currDir <- getCurrentDirectory
35+
36+
let findExe name = do
37+
mexe <- findExecutable name
38+
case mexe of
39+
Nothing -> error $ name ++ " not found on PATH"
40+
Just exe -> return exe
41+
runghc <- findExe "runghc"
42+
stack <- findExe "stack"
43+
44+
tests <- getDirectoryContents "tests" >>= filterM hasTest
45+
46+
envOrig <- getEnvironment
47+
48+
withSystemTempDirectory ("stack-integration-home") $ \newHome -> do
49+
let env' = Map.toList
50+
$ Map.insert "STACK_EXE" stack
51+
$ Map.insert "HOME" newHome
52+
$ Map.insert "APPDATA" newHome
53+
$ Map.delete "GHC_PACKAGE_PATH"
54+
$ Map.fromList
55+
$ map (first (map toUpper)) envOrig
56+
57+
origStackRoot <- getAppUserDataDirectory "stack"
58+
59+
hspec $ mapM_ (test runghc env' currDir origStackRoot newHome) tests
60+
61+
hasTest :: FilePath -> IO Bool
62+
hasTest dir = doesFileExist $ "tests" </> dir </> "Main.hs"
63+
64+
test :: FilePath -- ^ runghc
65+
-> [(String, String)] -- ^ env
66+
-> FilePath -- ^ currdir
67+
-> FilePath -- ^ origStackRoot
68+
-> FilePath -- ^ newHome
69+
-> String
70+
-> Spec
71+
test runghc env' currDir origStackRoot newHome name = it name $ withDir $ \dir -> do
72+
removeDirectoryRecursive newHome
73+
copyStackRoot origStackRoot (newHome </> takeFileName origStackRoot)
74+
let testDir = currDir </> "tests" </> name
75+
mainFile = testDir </> "Main.hs"
76+
libDir = currDir </> "lib"
77+
cp = (proc runghc
78+
[ "-clear-package-db"
79+
, "-global-package-db"
80+
, "-i" ++ libDir
81+
, mainFile
82+
])
83+
{ cwd = Just dir
84+
, env = Just env'
85+
}
86+
(ClosedStream, outSrc, errSrc, sph) <- streamingProcess cp
87+
(out, err, ec) <- runConcurrently $ (,,)
88+
<$> Concurrently (outSrc $$ sinkLbs)
89+
<*> Concurrently (errSrc $$ sinkLbs)
90+
<*> Concurrently (waitForStreamingProcess sph)
91+
when (ec /= ExitSuccess) $ throwIO $ TestFailure out err ec
92+
where
93+
withDir = withSystemTempDirectory ("stack-integration-" ++ name)
94+
95+
data TestFailure = TestFailure L.ByteString L.ByteString ExitCode
96+
deriving Typeable
97+
instance Show TestFailure where
98+
show (TestFailure out err ec) = concat
99+
[ "Exited with " ++ show ec
100+
, "\n\nstdout:\n"
101+
, toStr out
102+
, "\n\nstderr:\n"
103+
, toStr err
104+
]
105+
where
106+
toStr = TL.unpack . TL.decodeUtf8With lenientDecode
107+
instance Exception TestFailure
108+
109+
copyStackRoot :: FilePath -> FilePath -> IO ()
110+
copyStackRoot src dst =
111+
runResourceT $ sourceDirectoryDeep False src $$ CL.mapM_ go
112+
where
113+
go srcfp = when toCopy $ liftIO $ do
114+
Just suffix <- return $ stripPrefix src srcfp
115+
let dstfp = dst ++ "/" ++ suffix
116+
createDirectoryIfMissing True $ takeDirectory dstfp
117+
createSymbolicLink srcfp dstfp `catch` \(_ :: IOException) ->
118+
copyFile srcfp dstfp -- for Windows
119+
where
120+
toCopy = any (`isSuffixOf` srcfp)
121+
-- FIXME command line parameters to control how many of these get
122+
-- copied, trade-off of runtime/bandwidth vs isolation of tests
123+
[ ".tar"
124+
, ".xz"
125+
-- , ".gz"
126+
, ".7z.exe"
127+
, "00-index.cache"
128+
]

test/integration/lib/StackTest.hs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
module StackTest where
2+
3+
import Control.Exception
4+
import System.Environment
5+
import System.FilePath
6+
import System.Directory
7+
import System.IO
8+
import System.Process
9+
import System.Exit
10+
import System.Environment
11+
12+
stack' :: [String] -> IO ExitCode
13+
stack' args = do
14+
stack <- getEnv "STACK_EXE"
15+
putStrLn $ "Running stack with args " ++ show args
16+
(Nothing, Nothing, Nothing, ph) <- createProcess (proc stack args)
17+
waitForProcess ph
18+
19+
stack :: [String] -> IO ()
20+
stack args = do
21+
ec <- stack' args
22+
if ec == ExitSuccess
23+
then return ()
24+
else error $ "Exited with exit code: " ++ show ec
25+
26+
stackErr :: [String] -> IO ()
27+
stackErr args = do
28+
ec <- stack' args
29+
if ec == ExitSuccess
30+
then error "stack was supposed to fail, but didn't"
31+
else return ()
32+
33+
doesNotExist :: FilePath -> IO ()
34+
doesNotExist fp = do
35+
putStrLn $ "doesNotExist " ++ fp
36+
isFile <- doesFileExist fp
37+
if isFile
38+
then error $ "File exists: " ++ fp
39+
else do
40+
isDir <- doesDirectoryExist fp
41+
if isDir
42+
then error $ "Directory exists: " ++ fp
43+
else return ()
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: stack-integration-tests
2+
version: 0.0.0
3+
synopsis: Integration tests
4+
description: Integration tests
5+
license: BSD3
6+
license-file: LICENSE
7+
author: Chris Done
8+
maintainer: [email protected]
9+
category: Development
10+
build-type: Simple
11+
cabal-version: >=1.10
12+
homepage: https://github.com/commercialhaskell/stack
13+
14+
-- This is only present because cabal won't allow a test suite without a
15+
-- library or executable
16+
executable stack-integration-dummy
17+
main-is: Dummy.hs
18+
build-depends: base
19+
default-language: Haskell2010
20+
21+
test-suite stack-integration-tests
22+
type: exitcode-stdio-1.0
23+
main-is: Spec.hs
24+
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
25+
26+
build-depends: base >= 4.7 && < 10
27+
, temporary
28+
, hspec
29+
, process
30+
, filepath
31+
, directory
32+
, text
33+
, unix-compat
34+
, containers
35+
, conduit
36+
, conduit-extra
37+
, resourcet
38+
, async
39+
, transformers
40+
, bytestring
41+
default-language: Haskell2010
42+
43+
source-repository head
44+
type: git
45+
location: https://github.com/commercialhaskell/stack

test/integration/stack.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
packages:
2+
- '.'
3+
resolver: lts-2.9
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import StackTest
2+
3+
main :: IO ()
4+
main = do
5+
stack ["install", "acme-missiles-0.3"]
6+
doesNotExist "stack.yaml"

0 commit comments

Comments
 (0)