Add postgrest-coverage to show and upload hpc reports to codecov

This commit is contained in:
Wolfgang Walther
2020-12-30 13:22:25 +01:00
committed by GitHub
parent 2015688312
commit c7f0d42323
17 changed files with 179 additions and 69 deletions
+7
View File
@@ -234,6 +234,13 @@ jobs:
name: Run memory tests name: Run memory tests
command: postgrest-test-memory command: postgrest-test-memory
when: always when: always
- run:
name: Run coverage
command: postgrest-coverage
- run:
name: Upload coverage to codecov
command: bash <(curl -s https://codecov.io/bash) -f coverage/codecov.json
workflows: workflows:
version: 2 version: 2
+17
View File
@@ -0,0 +1,17 @@
codecov:
branch: master
comment: false
coverage:
status:
project:
default:
target: auto
threshold: 0%
only_pulls: false
patch:
default:
target: auto
threshold: 0%
only_pulls: true
+3
View File
@@ -18,3 +18,6 @@ dist-newstyle
postgrest.hp postgrest.hp
postgrest.prof postgrest.prof
__pycache__ __pycache__
*.tix
coverage
.hpc
+7 -3
View File
@@ -66,7 +66,7 @@ let
# Options passed to cabal in dev tools and tests # Options passed to cabal in dev tools and tests
devCabalOptions = devCabalOptions =
"-f FailOnWarn --test-show-detail=direct"; "-f dev --test-show-detail=direct";
profiledHaskellPackages = profiledHaskellPackages =
pkgs.haskell.packages."${compiler}".extend (self: super: pkgs.haskell.packages."${compiler}".extend (self: super:
@@ -87,7 +87,7 @@ rec {
# libraries and documentation. We disable running the test suite on Nix # libraries and documentation. We disable running the test suite on Nix
# builds, as they require a database to be set up. # builds, as they require a database to be set up.
postgrestPackage = postgrestPackage =
lib.dontCheck (lib.enableCabalFlag postgrest "FailOnWarn"); lib.dontCheck postgrest;
# Static executable. # Static executable.
postgrestStatic = postgrestStatic =
@@ -114,7 +114,11 @@ rec {
# Scripts for running tests. # Scripts for running tests.
tests = tests =
pkgs.callPackage nix/tests.nix { inherit postgrest postgrestStatic postgrestProfiled postgresqlVersions devCabalOptions; }; pkgs.callPackage nix/tests.nix {
inherit postgrest postgrestProfiled postgresqlVersions devCabalOptions;
ghc = pkgs.haskell.compiler."${compiler}";
hpc-codecov = pkgs.haskell.packages."${compiler}".hpc-codecov;
};
# Linting and styling scripts. # Linting and styling scripts.
style = style =
+2
View File
@@ -72,6 +72,8 @@ let
} }
'' ''
${cabal-install}/bin/cabal v2-clean ${cabal-install}/bin/cabal v2-clean
# clean old coverage data, too
rm -rf .hpc coverage
''; '';
check = check =
+89 -3
View File
@@ -5,15 +5,15 @@
, checkedShellScript , checkedShellScript
, curl , curl
, devCabalOptions , devCabalOptions
, diffutils , ghc
, gnugrep
, haskell , haskell
, hpc-codecov
, lib , lib
, postgresql , postgresql
, postgresqlVersions , postgresqlVersions
, postgrest , postgrest
, postgrestProfiled , postgrestProfiled
, postgrestStatic
, procps
, python3 , python3
, runtimeShell , runtimeShell
, yq , yq
@@ -164,6 +164,90 @@ let
postgrest --dump-schema \ postgrest --dump-schema \
| ${yq}/bin/yq -y . | ${yq}/bin/yq -y .
''; '';
coverage =
name: postgresql:
checkedShellScript
{
inherit name;
docs = "Run spec and io tests while collecting hpc coverage data.";
inRootDir = true;
}
''
env="$(cat ${postgrest.env})"
export PATH="$env/bin:$PATH"
# clean up previous coverage reports
mkdir -p coverage
rm -rf coverage/*
# temporary directory to collect data in
tmpdir="$(mktemp -d)"
# we keep the tmpdir when an error occurs for debugging and only remove it on success
trap 'echo Temporary directory kept at: $tmpdir' ERR SIGINT SIGTERM
# build once before running all the tests
${cabal-install}/bin/cabal v2-build ${devCabalOptions} --enable-tests all
# collect all tests
HPCTIXFILE="$tmpdir"/io.tix \
${withTmpDb postgresql} ${cabal-install}/bin/cabal v2-exec ${devCabalOptions} \
${ioTestPython}/bin/pytest -- -v test/io-tests
HPCTIXFILE="$tmpdir"/spec.tix \
${withTmpDb postgresql} ${cabal-install}/bin/cabal v2-test ${devCabalOptions}
# collect all the tix files
${ghc}/bin/hpc sum --union --exclude=Paths_postgrest --output="$tmpdir"/tests.tix "$tmpdir"/io.tix "$tmpdir"/spec.tix
# prepare the overlay
${ghc}/bin/hpc overlay --output="$tmpdir"/overlay.tix test/coverage.overlay
${ghc}/bin/hpc sum --union --output="$tmpdir"/tests-overlay.tix "$tmpdir"/tests.tix "$tmpdir"/overlay.tix
# check nothing in the overlay is actually tested
${ghc}/bin/hpc map --function=inv --output="$tmpdir"/inverted.tix "$tmpdir"/tests.tix
${ghc}/bin/hpc combine --function=sub \
--output="$tmpdir"/check.tix "$tmpdir"/overlay.tix "$tmpdir"/inverted.tix
# returns zero exit code if any count="<non-zero>" lines are found, i.e.
# something is covered by both the overlay and the tests
if ${ghc}/bin/hpc report --xml "$tmpdir"/check.tix | ${gnugrep}/bin/grep -qP 'count="[^0]'
then
${ghc}/bin/hpc markup --highlight-covered --destdir=coverage/overlay "$tmpdir"/overlay.tix || true
${ghc}/bin/hpc markup --highlight-covered --destdir=coverage/check "$tmpdir"/check.tix || true
echo "ERROR: Something is covered by both the tests and the overlay:"
echo "file://$(pwd)/coverage/check/hpc_index.html"
exit 1
else
# copy the result .tix file to the coverage/ dir to make it available to postgrest-coverage-draft-overlay, too
cp "$tmpdir"/tests-overlay.tix coverage/postgrest.tix
# prepare codecov json report
${hpc-codecov}/bin/hpc-codecov --mix=.hpc --out=coverage/codecov.json coverage/postgrest.tix
# create html and stdout reports
# TODO: The markup command fails when run outside nix-shell (i.e. in CI!)
# Need to fix it properly in the future instead of adding the || true
${ghc}/bin/hpc markup --destdir=coverage coverage/postgrest.tix || true
echo "file://$(pwd)/coverage/hpc_index.html"
${ghc}/bin/hpc report coverage/postgrest.tix "$@"
fi
rm -rf "$tmpdir"
'';
coverageDraftOverlay =
name:
checkedShellScript
{
inherit name;
docs = "Create a draft overlay from current coverage report.";
inRootDir = true;
}
''
${ghc}/bin/hpc draft --output=test/coverage.overlay coverage/postgrest.tix
sed -i 's|^module \(.*\):|module \1/|g' test/coverage.overlay
'';
in in
# Create an environment that contains all the utility scripts for running tests # Create an environment that contains all the utility scripts for running tests
# that we defined above. # that we defined above.
@@ -179,6 +263,8 @@ buildEnv
testSpecAllVersions.bin testSpecAllVersions.bin
(testIO "postgrest-test-io" postgresql).bin (testIO "postgrest-test-io" postgresql).bin
(dumpSchema "postgrest-dump-schema" postgresql).bin (dumpSchema "postgrest-dump-schema" postgresql).bin
(coverage "postgrest-coverage" postgresql).bin
(coverageDraftOverlay "postgrest-coverage-draft-overlay").bin
] ++ testSpecVersions; ] ++ testSpecVersions;
} }
# The memory tests have large dependencies (a profiled build of PostgREST) # The memory tests have large dependencies (a profiled build of PostgREST)
+38 -29
View File
@@ -19,12 +19,17 @@ source-repository head
type: git type: git
location: git://github.com/PostgREST/postgrest.git location: git://github.com/PostgREST/postgrest.git
flag FailOnWarn flag dev
default: False default: False
manual: True manual: True
description: No warnings allowed description: Development flags
library library
default-language: Haskell2010
default-extensions: OverloadedStrings
QuasiQuotes
NoImplicitPrelude
hs-source-dirs: src
exposed-modules: PostgREST.ApiRequest exposed-modules: PostgREST.ApiRequest
PostgREST.App PostgREST.App
PostgREST.Auth PostgREST.Auth
@@ -43,7 +48,6 @@ library
PostgREST.Private.Common PostgREST.Private.Common
PostgREST.Private.ProxyUri PostgREST.Private.ProxyUri
PostgREST.Private.QueryFragment PostgREST.Private.QueryFragment
hs-source-dirs: src
build-depends: base >= 4.9 && < 4.15 build-depends: base >= 4.9 && < 4.15
, HTTP >= 4000.3.7 && < 4000.4 , HTTP >= 4000.3.7 && < 4000.4
, Ranged-sets >= 0.3 && < 0.5 , Ranged-sets >= 0.3 && < 0.5
@@ -88,15 +92,12 @@ library
, wai-extra >= 3.0.19 && < 3.2 , wai-extra >= 3.0.19 && < 3.2
, wai-logger >= 2.3.2 , wai-logger >= 2.3.2
, wai-middleware-static >= 0.8.1 && < 0.10 , wai-middleware-static >= 0.8.1 && < 0.10
default-language: Haskell2010 if flag(dev)
default-extensions: OverloadedStrings ghc-options: -O0 -Werror -Wall -fwarn-identities
QuasiQuotes
NoImplicitPrelude
if flag(FailOnWarn)
ghc-options: -O2 -Werror -Wall -fwarn-identities
-fno-spec-constr -optP-Wno-nonportable-include-path -fno-spec-constr -optP-Wno-nonportable-include-path
-fhpc -hpcdir .hpc
else else
ghc-options: -O2 -Wall -fwarn-identities ghc-options: -O2 -Werror -Wall -fwarn-identities
-fno-spec-constr -optP-Wno-nonportable-include-path -fno-spec-constr -optP-Wno-nonportable-include-path
-- -fno-spec-constr may help keep compile time memory use in check, -- -fno-spec-constr may help keep compile time memory use in check,
-- see https://gitlab.haskell.org/ghc/ghc/issues/16017#note_219304 -- see https://gitlab.haskell.org/ghc/ghc/issues/16017#note_219304
@@ -105,8 +106,12 @@ library
-- see https://github.com/commercialhaskell/stack/issues/3918 -- see https://github.com/commercialhaskell/stack/issues/3918
executable postgrest executable postgrest
main-is: Main.hs default-language: Haskell2010
default-extensions: OverloadedStrings
QuasiQuotes
NoImplicitPrelude
hs-source-dirs: main hs-source-dirs: main
main-is: Main.hs
build-depends: base >= 4.9 && < 4.15 build-depends: base >= 4.9 && < 4.15
, aeson >= 1.4.7 && < 1.6 , aeson >= 1.4.7 && < 1.6
, auto-update >= 0.1.4 && < 0.2 , auto-update >= 0.1.4 && < 0.2
@@ -126,17 +131,14 @@ executable postgrest
, time >= 1.6 && < 1.11 , time >= 1.6 && < 1.11
, wai >= 3.2.1 && < 3.3 , wai >= 3.2.1 && < 3.3
, warp >= 3.2.12 && < 3.4 , warp >= 3.2.12 && < 3.4
default-language: Haskell2010 if flag(dev)
default-extensions: OverloadedStrings
QuasiQuotes
NoImplicitPrelude
if flag(FailOnWarn)
ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I2" ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I2"
-O2 -Werror -Wall -fwarn-identities -O0 -Werror -Wall -fwarn-identities
-fno-spec-constr -optP-Wno-nonportable-include-path -fno-spec-constr -optP-Wno-nonportable-include-path
-fhpc -hpcdir .hpc
else else
ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I2" ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I2"
-O2 -Wall -fwarn-identities -O2 -Werror -Wall -fwarn-identities
-fno-spec-constr -optP-Wno-nonportable-include-path -fno-spec-constr -optP-Wno-nonportable-include-path
if !os(windows) if !os(windows)
@@ -145,6 +147,11 @@ executable postgrest
test-suite spec test-suite spec
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
default-language: Haskell2010
default-extensions: OverloadedStrings
QuasiQuotes
NoImplicitPrelude
hs-source-dirs: test
main-is: Main.hs main-is: Main.hs
other-modules: Feature.AndOrParamsSpec other-modules: Feature.AndOrParamsSpec
Feature.AsymmetricJwtSpec Feature.AsymmetricJwtSpec
@@ -179,7 +186,6 @@ test-suite spec
Feature.UpsertSpec Feature.UpsertSpec
SpecHelper SpecHelper
TestTypes TestTypes
hs-source-dirs: test
build-depends: base >= 4.9 && < 4.15 build-depends: base >= 4.9 && < 4.15
, aeson >= 1.4.7 && < 1.6 , aeson >= 1.4.7 && < 1.6
, aeson-qq >= 0.8.1 && < 0.9 , aeson-qq >= 0.8.1 && < 0.9
@@ -211,20 +217,21 @@ test-suite spec
, transformers-base >= 0.4.4 && < 0.5 , transformers-base >= 0.4.4 && < 0.5
, wai >= 3.2.1 && < 3.3 , wai >= 3.2.1 && < 3.3
, wai-extra >= 3.0.19 && < 3.2 , wai-extra >= 3.0.19 && < 3.2
ghc-options: -threaded -rtsopts -with-rtsopts=-N
-O0 -Werror -Wall -fwarn-identities
-fno-spec-constr -optP-Wno-nonportable-include-path
-fno-warn-missing-signatures
test-suite spec-querycost
type: exitcode-stdio-1.0
default-language: Haskell2010 default-language: Haskell2010
default-extensions: OverloadedStrings default-extensions: OverloadedStrings
QuasiQuotes QuasiQuotes
NoImplicitPrelude NoImplicitPrelude
ghc-options: -threaded -rtsopts -with-rtsopts=-N hs-source-dirs: test
main-is: QueryCost.hs
Test-Suite spec-querycost other-modules: SpecHelper
Type: exitcode-stdio-1.0 build-depends: base >= 4.9 && < 4.15
Default-Language: Haskell2010
default-extensions: OverloadedStrings, QuasiQuotes, NoImplicitPrelude
Hs-Source-Dirs: test
Main-Is: QueryCost.hs
Other-Modules: SpecHelper
Build-Depends: base >= 4.9 && < 4.15
, aeson >= 1.4.7 && < 1.6 , aeson >= 1.4.7 && < 1.6
, aeson-qq >= 0.8.1 && < 0.9 , aeson-qq >= 0.8.1 && < 0.9
, async >= 2.1.1 && < 2.3 , async >= 2.1.1 && < 2.3
@@ -256,3 +263,5 @@ Test-Suite spec-querycost
, transformers-base >= 0.4.4 && < 0.5 , transformers-base >= 0.4.4 && < 0.5
, wai >= 3.2.1 && < 3.3 , wai >= 3.2.1 && < 3.3
, wai-extra >= 3.0.19 && < 3.2 , wai-extra >= 3.0.19 && < 3.2
ghc-options: -O0 -Werror -Wall -fwarn-identities
-fno-spec-constr -optP-Wno-nonportable-include-path
-4
View File
@@ -1,9 +1,5 @@
resolver: lts-16.26 # 2020-12-13, GHC 8.8.4 resolver: lts-16.26 # 2020-12-13, GHC 8.8.4
flags:
postgrest:
FailOnWarn: true
nix: nix:
packages: packages:
- pcre - pcre
-7
View File
@@ -1,14 +1,10 @@
module Feature.InsertSpec where module Feature.InsertSpec where
import qualified Data.Aeson as JSON
import Data.List (lookup) import Data.List (lookup)
import Data.Maybe (fromJust)
import Network.Wai (Application) import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus)) import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
import Test.Hspec hiding (pendingWith) import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai.Matcher (bodyEquals) import Test.Hspec.Wai.Matcher (bodyEquals)
import TestTypes (CompoundPK (..), IncPK (..))
import Network.HTTP.Types import Network.HTTP.Types
import Test.Hspec.Wai import Test.Hspec.Wai
@@ -441,9 +437,6 @@ spec actualPgVersion = do
describe "Row level permission" $ describe "Row level permission" $
it "set user_id when inserting rows" $ do it "set user_id when inserting rows" $ do
post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |]
post "/postgrest/users" [json| { "id":"jroe", "pass": "1234", "role": "postgrest_test_author" } |]
request methodPost "/authors_only" request methodPost "/authors_only"
[ authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.B-lReuGNDwAlU1GOC476MlO0vAt9JNoHIlxg2vwMaO0", ("Prefer", "return=representation") ] [ authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.B-lReuGNDwAlU1GOC476MlO0vAt9JNoHIlxg2vwMaO0", ("Prefer", "return=representation") ]
[json| { "secret": "nyancat" } |] [json| { "secret": "nyancat" } |]
-1
View File
@@ -1,7 +1,6 @@
module Feature.QueryLimitedSpec where module Feature.QueryLimitedSpec where
import Network.Wai (Application) import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleHeaders, simpleStatus))
import Network.HTTP.Types import Network.HTTP.Types
import Test.Hspec import Test.Hspec
-1
View File
@@ -8,7 +8,6 @@ import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper
-- two helpers functions to make sure that each test can setup and cleanup properly -- two helpers functions to make sure that each test can setup and cleanup properly
-5
View File
@@ -1,18 +1,13 @@
module Feature.RpcPreRequestGucsSpec where module Feature.RpcPreRequestGucsSpec where
import qualified Data.ByteString.Lazy as BL (empty)
import Network.Wai (Application) import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
import Network.HTTP.Types import Network.HTTP.Types
import Test.Hspec hiding (pendingWith) import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import Text.Heredoc
import Protolude hiding (get, put) import Protolude hiding (get, put)
import SpecHelper
spec :: SpecWith ((), Application) spec :: SpecWith ((), Application)
spec = spec =
-1
View File
@@ -8,7 +8,6 @@ import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application) spec :: SpecWith ((), Application)
spec = spec =
-3
View File
@@ -1,14 +1,11 @@
module Feature.UpdateSpec where module Feature.UpdateSpec where
import Data.List (lookup)
import Network.Wai (Application) import Network.Wai (Application)
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
import Test.Hspec hiding (pendingWith) import Test.Hspec hiding (pendingWith)
import Network.HTTP.Types import Network.HTTP.Types
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import Text.Heredoc
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
-1
View File
@@ -5,7 +5,6 @@ import qualified Data.Aeson.Lens as L
import qualified Hasql.Decoders as HD import qualified Hasql.Decoders as HD
import qualified Hasql.DynamicStatements.Snippet as H import qualified Hasql.DynamicStatements.Snippet as H
import qualified Hasql.DynamicStatements.Statement as H import qualified Hasql.DynamicStatements.Statement as H
import qualified Hasql.Encoders as HE
import qualified Hasql.Pool as P import qualified Hasql.Pool as P
import qualified Hasql.Statement as H import qualified Hasql.Statement as H
import qualified Hasql.Transaction as HT import qualified Hasql.Transaction as HT
View File
+7 -2
View File
@@ -75,13 +75,16 @@ def defaultenv():
def dumpconfig(configpath=None, env=None, stdin=None): def dumpconfig(configpath=None, env=None, stdin=None):
"Dump the config as parsed by PostgREST." "Dump the config as parsed by PostgREST."
env = env or {}
command = [POSTGREST_BIN, "--dump-config"] command = [POSTGREST_BIN, "--dump-config"]
env["HPCTIXFILE"] = os.getenv("HPCTIXFILE", "")
if configpath: if configpath:
command.append(configpath) command.append(configpath)
process = subprocess.Popen( process = subprocess.Popen(
command, env=env or {}, stdin=subprocess.PIPE, stdout=subprocess.PIPE command, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE
) )
process.stdin.write(stdin or b"") process.stdin.write(stdin or b"")
@@ -96,6 +99,7 @@ def dumpconfig(configpath=None, env=None, stdin=None):
@contextlib.contextmanager @contextlib.contextmanager
def run(configpath=None, stdin=None, env=None, port=None): def run(configpath=None, stdin=None, env=None, port=None):
"Run PostgREST and yield an endpoint that is ready for connections." "Run PostgREST and yield an endpoint that is ready for connections."
env = env or {}
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
if port: if port:
@@ -108,11 +112,12 @@ def run(configpath=None, stdin=None, env=None, port=None):
baseurl = "http+unix://" + urllib.parse.quote_plus(str(socketfile)) baseurl = "http+unix://" + urllib.parse.quote_plus(str(socketfile))
command = [POSTGREST_BIN] command = [POSTGREST_BIN]
env["HPCTIXFILE"] = os.getenv("HPCTIXFILE", "")
if configpath: if configpath:
command.append(configpath) command.append(configpath)
process = subprocess.Popen(command, stdin=subprocess.PIPE, env=env or {}) process = subprocess.Popen(command, stdin=subprocess.PIPE, env=env)
try: try:
process.stdin.write(stdin or b"") process.stdin.write(stdin or b"")