Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
23 changes: 22 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ jobs:
path: |
${{ steps.setup-haskell-cabal.outputs.cabal-store }}
dist-newstyle
key: ${{ runner.os }}-latest
key: ${{ runner.os }}-latest-bounds-checking
- name: Test
run: cabal test --ghc-options='-fcheck-prim-bounds -fno-ignore-asserts -DHS_BYTESTRING_ASSERTIONS'

Expand All @@ -183,6 +183,27 @@ jobs:
- name: Test
run: cabal test -fpure-haskell --ghc-options=-fno-ignore-asserts --enable-tests --test-show-details=direct all

inspection-testing:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: haskell-actions/setup@v2
id: setup-haskell-cabal
with:
ghc-version: 'latest'
- name: Update cabal package database
run: cabal update
- uses: actions/cache@v3
name: Cache cabal stuff
with:
path: |
${{ steps.setup-haskell-cabal.outputs.cabal-store }}
dist-newstyle
key: ${{ runner.os }}-latest-inspection-testing
- name: Test
run: sh run-plugin-tests.sh

i386:
needs: build
runs-on: ubuntu-latest
Expand Down
25 changes: 23 additions & 2 deletions bytestring.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ Flag pure-haskell
default: False
manual: True

Flag plugin-tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is somewhat suboptimal, because Hackage won't have any idea that this is an internal flag for tests only and will proudly advertise the flag to everyone. Given that we hack the Cabal file anyway, could we insert build-depends and other-modules as a part of shell script too?

description: Also build and run the tests that use plugins.

(Due to circular dependencies, these tests currently
cannot be built without renaming the library.)
default: False
manual: True

source-repository head
type: git
location: https://github.com/haskell/bytestring
Expand Down Expand Up @@ -216,16 +224,27 @@ test-suite bytestring-tests
QuickCheckUtils
hs-source-dirs: tests,
tests/builder
build-depends: bytestring
-- Keep 'bytestring' on the same line as 'build-depends:'
-- this is used by our hack to allow plugin tests
build-depends: base,
bytestring,
deepseq,
QuickCheck,
tasty,
tasty-expected-failure ^>= 0.12.3,
tasty-quickcheck >= 0.8.1,
template-haskell,
transformers >= 0.3,
syb

if flag(plugin-tests)
cpp-options: -DBYTESTRING_PLUGIN_TESTS=1
build-depends: tasty-inspection-testing ^>= 0.2.1
other-modules: PluginTests
PluginTests.Splices
else
cpp-options: -DBYTESTRING_PLUGIN_TESTS=0

ghc-options: -fwarn-unused-binds
-rtsopts
if !arch(wasm32)
Expand All @@ -249,8 +268,10 @@ benchmark bytestring-bench

ghc-options: -O2 "-with-rtsopts=-A32m"
-fproc-alignment=64
build-depends: bytestring
-- Keep 'bytestring' on the same line as 'build-depends:'
-- this is used by our hack to allow plugin tests
build-depends: base,
bytestring,
deepseq,
tasty-bench,
random
11 changes: 11 additions & 0 deletions run-plugin-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# small script to hackily work around the dependency cycle
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you feel about chmod +x on this file?

# 'bytestring -> [plugin] -> ghc -> bytestring' that prevents
# the testsuite from using plugins, by renaming the library
# to 'bytestring-plugins-hack'

sed -E '/Name:|build-depends:/s/bytestring/bytestring-plugins-hack/' bytestring.cabal > bytestring-plugins-hack.cabal

mv bytestring.cabal bytestring.cabal.__BACKUP__
cabal test -fplugin-tests --test-show-details=direct "$@"
mv bytestring.cabal.__BACKUP__ bytestring.cabal
rm bytestring-plugins-hack.cabal
8 changes: 8 additions & 0 deletions tests/Main.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{-# LANGUAGE CPP #-}

module Main (main) where

import Test.Tasty
Expand All @@ -7,6 +9,9 @@ import qualified IsValidUtf8
import qualified LazyHClose
import qualified Lift
import qualified Properties
#if BYTESTRING_PLUGIN_TESTS
import qualified PluginTests
#endif

main :: IO ()
main = defaultMain $ testGroup "All"
Expand All @@ -15,4 +20,7 @@ main = defaultMain $ testGroup "All"
, LazyHClose.testSuite
, Lift.testSuite
, Properties.testSuite
#if BYTESTRING_PLUGIN_TESTS
, PluginTests.testSuite
#endif
]
98 changes: 98 additions & 0 deletions tests/PluginTests.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{-# LANGUAGE TemplateHaskell #-}

{-# OPTIONS_GHC -O -dsuppress-all -dno-suppress-type-signatures -fplugin=Test.Tasty.Inspection.Plugin #-}

module PluginTests (testSuite) where

import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BL8
import Data.ByteString.Builder
import Data.Word

import Test.Tasty
import Test.Tasty.ExpectedFailure
import Test.Tasty.Inspection

import PluginTests.Splices

testSuite :: TestTree
testSuite = testGroup "Inspection plugin tests"
[ testGroup "Literals" $
[ testGroup "StrictByteString"
[ $(hasNoStringyStuff 'pack_strict_foo)
, $(inspectTest $ 'len_pack_strict_foo === 'literal_three)
, $(hasNoStringyStuff 'pack_strict_literal)
, $(inspectTest $ 'len_pack_strict_literal === 'literal_thirtyOne)
, expectFail $ $(hasNoStringyStuff 'pack_strict_nonAscii)
, expectFail $ $(hasNoStringyStuff 'pack_strict_literal_nonAscii)
]

, testGroup "Builder"
[ $(hasNoStringyStuff 'builder_string8_foo)
, $(hasNoStringyStuff 'builder_string8_literal)
, $(hasNoStringyStuff 'builder_stringUtf8_foo)
, $(hasNoStringyStuff 'builder_stringUtf8_literal)
, $(hasNoStringyStuff 'builder_stringUtf8_nonAscii)
, $(hasNoStringyStuff 'builder_stringUtf8_literal_nonAscii)
]
]

, $(inspectTest $ 'append_pack_replicate_unboxing `hasNoType` ''S.ByteString)
]

foo_string_literal :: [Char]
foo_string_literal = "foo"

unicode_string_literal :: [Char]
unicode_string_literal = "\0example\0 ... \xff \x1f530"

pack_strict_foo :: S.ByteString
pack_strict_foo = S8.pack foo_string_literal

len_pack_strict_foo :: Int
len_pack_strict_foo = S.length pack_strict_foo

pack_strict_literal :: S.ByteString
pack_strict_literal = S8.pack "some ascii literal of length 31"

len_pack_strict_literal :: Int
len_pack_strict_literal = S.length pack_strict_literal

pack_strict_nonAscii :: S.ByteString
pack_strict_nonAscii = S8.pack unicode_string_literal

pack_strict_literal_nonAscii :: S.ByteString
pack_strict_literal_nonAscii
= S8.pack "this\0literal contains\x80\xf0\xff non-ascii characters"

literal_three :: Int
literal_three = 3

literal_thirtyOne :: Int
literal_thirtyOne = 31

builder_string8_foo :: Builder
builder_string8_foo = string8 foo_string_literal

builder_string8_literal :: Builder
builder_string8_literal = string8 "some ascii string literal"

builder_stringUtf8_foo :: Builder
builder_stringUtf8_foo = stringUtf8 foo_string_literal

builder_stringUtf8_literal :: Builder
builder_stringUtf8_literal = stringUtf8 "some other ascii string literal"

builder_stringUtf8_nonAscii :: Builder
builder_stringUtf8_nonAscii = stringUtf8 unicode_string_literal

builder_stringUtf8_literal_nonAscii :: Builder
builder_stringUtf8_literal_nonAscii
= stringUtf8 "inline literal string containing \0special\0 and non-ASCII characters like \x2139"

append_pack_replicate_unboxing :: Int -> Word8 -> Int
append_pack_replicate_unboxing n c
= S.count c $ S.append (S.pack [0..c]) (S.replicate n c)

45 changes: 45 additions & 0 deletions tests/PluginTests/Splices.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{-# LANGUAGE TemplateHaskellQuotes #-}

module PluginTests.Splices where
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add an explicit export list?


import Test.Tasty
import Test.Tasty.Inspection
import Language.Haskell.TH

import GHC.Base
( unpackCString#
, unpackAppendCString#
, unpackCStringUtf8#
, unpackAppendCStringUtf8#
, unpackNBytes#
, unpackFoldrCString#
, unpackFoldrCStringUtf8#
)
import Language.Haskell.TH (Name)

unpackCString_functions_without_foldr :: [Name]
unpackCString_functions_without_foldr =
[ 'unpackCString#
, 'unpackAppendCString#
, 'unpackCStringUtf8#
, 'unpackAppendCStringUtf8#
, 'unpackNBytes#
]

unpackCString_functions_all :: [Name]
unpackCString_functions_all =
[ 'unpackFoldrCString#
, 'unpackFoldrCStringUtf8#
] ++ unpackCString_functions_without_foldr

hasNoStringyStuff :: Name -> Q Exp
hasNoStringyStuff n = flip inspectObligations n
[ (`hasNoTypes` [''Char, ''[]])
, (`doesNotUseAnyOf` unpackCString_functions_all)
]

hasNoStringyStuffExceptFolds :: Name -> Q Exp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one seems unused anywhere?

hasNoStringyStuffExceptFolds n = flip inspectObligations n
[ (`hasNoTypes` [''Char, ''[]])
, (`doesNotUseAnyOf` unpackCString_functions_without_foldr)
]
Loading