Skip to content

Commit

Permalink
Give each example a cabal test suite
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-martin committed Feb 24, 2022
1 parent 23f6b27 commit 2b0aa44
Show file tree
Hide file tree
Showing 65 changed files with 755 additions and 583 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@ jobs:
- uses: cachix/cachix-action@v10
with:
name: typeclasses
skipPush: true

- run: ./tools/test.hs
- run: nix-shell --pure --run 'cabal build all'

- run: nix-shell --pure --run 'cabal test all'
7 changes: 6 additions & 1 deletion cabal.project
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
packages: tools/phrasebook.cabal
packages: ./phrasebook.cabal

constraints:
hashable >= 1.3.5
, optics >= 0.4
, relude >= 1
49 changes: 31 additions & 18 deletions docs/build-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@
title: How to build and run the Phrasebook examples
---

## Haskell language server
## Tools to use while editing

### Haskell language server

If you are using Visual Studio Code, we recommend installing the [Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) extension. Error messages and other helpful annotations will then appear in the editor.

## ghcid
### ghcid

If you are not using an editor with integrated language support, [ghcid](https://typeclasses.com/ghci/ghcid) is a good alternative.

```sh
$ ghcid --command 'cabal repl hello-world'
```

## GHCi
## Experimentation and testing

### Running a program

There are two ways to run one of the example programs:

1. Run it directly using `runhaskell`. For example, `runhaskell hello-world.hs`. The program's dependencies must already be installed. See information about Nix below to make that easier.
2. Run using cabal. For example, `cabal run hello-world`.

### The REPL

To open a REPL, use the "cabal repl" command, giving as an argument the name of the program you want to load.

Expand All @@ -25,37 +36,39 @@ $ cabal repl hello-world
hello world
```

## Using Nix shell
### The test suites

You do not have to use Nix to run these Haskell programs, but you may find it convenient.

[Install Nix](https://nixos.org/nix/manual/#chap-installation),

Optionally, install [Cachix](https://cachix.org/) and add the `typeclasses` cache. This step is optional, but will greatly reduce build time.
To run the tests:

```sh
$ nix-env -iA 'cachix' -f 'https://cachix.org/api/v1/install'
$ cachix use 'typeclasses'
$ cabal test all
```

Within the Nix shell, you have all of the dependencies required by the examples in the Phrasebook. For example, you can run commands like `runhaskell` and `ghcid`.
The tests are also run automatically by [GitHub actions](https://github.com/typeclasses/haskell-phrasebook/actions).

## Nix

You do not have to use Nix to run these Haskell programs, but you may find it convenient. Within the Nix shell, you have all of the dependencies required by the examples in the Phrasebook. For example, you can run commands like `runhaskell` and `ghcid`.

```sh
$ nix-shell 'tools/shell.nix'
$ nix-shell

[nix-shell]$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 9.0.1
```

## Outputs
### Getting started with Nix

In addition to the code examples themselves, the results from running the examples are also included in this repository, in the `outputs` directory. An example's output is typically given the same name as its source file, with the extension changed; for example, the output of `hello-world.hs` is given by the file `outputs/hello-world.txt`.
[Install Nix](https://nixos.org/nix/manual/#chap-installation).

When any source code or dependency versions change, run `./tools/generate-outputs.hs`. This script runs all of the examples and updates the output files.
Optionally, install [Cachix](https://cachix.org/) and add the `typeclasses` cache. This step is optional, but will greatly reduce build time.

Any examples that include nondeterministic behavior (such as `threads.hs`) have the nondeterministic portion of their output redacted as "..." to avoid including non-repeatable results in the output files.
```sh
$ nix-env -iA 'cachix' -f 'https://cachix.org/api/v1/install'
$ cachix use 'typeclasses'
```

## Nix dependency versions
### Nix dependency versions

All of the Nix tools are configured to use a specific version of [the Nix package set](https://github.com/nixos/nixpkgs/) to ensure that the code works the same in all environments. This version is specified in `tools/versions.json`.

Expand Down
77 changes: 77 additions & 0 deletions hie.yaml
Original file line number Diff line number Diff line change
@@ -1,52 +1,129 @@
cradle:
cabal:
- path: "./tests/PhrasebookTesting.hs"
component: "lib:phrasebook-testing"

- path: "./bounded-queues.hs"
component: "exe:bounded-queues"
- path: "./tests/test-bounded-queues.hs"
component: "test:test-bounded-queues"

- path: "./branching.hs"
component: "exe:branching"
- path: "./tests/test-branching.hs"
component: "test:test-branching"

- path: "./common-types.hs"
component: "exe:common-types"
- path: "./tests/test-common-types.hs"
component: "test:test-common-types"

- path: "./crypto-hashing.hs"
component: "exe:crypto-hashing"
- path: "./tests/test-crypto-hashing.hs"
component: "test:test-crypto-hashing"

- path: "./dynamic.hs"
component: "exe:dynamic"
- path: "./tests/test-dynamic.hs"
component: "test:test-dynamic"

- path: "./enum-ranges.hs"
component: "exe:enum-ranges"
- path: "./tests/test-enum-ranges.hs"
component: "test:test-enum-ranges"

- path: "./file-handles.hs"
component: "exe:file-handles"
- path: "./tests/test-file-handles.hs"
component: "test:test-file-handles"

- path: "./folding-lists.hs"
component: "exe:folding-lists"
- path: "./tests/test-folding-lists.hs"
component: "test:test-folding-lists"

- path: "./for-loops.hs"
component: "exe:for-loops"
- path: "./tests/test-for-loops.hs"
component: "test:test-for-loops"

- path: "./functions.hs"
component: "exe:functions"
- path: "./tests/test-functions.hs"
component: "test:test-functions"

- path: "./guard.hs"
component: "exe:guard"
- path: "./tests/test-guard.hs"
component: "test:test-guard"

- path: "./hashing.hs"
component: "exe:hashing"
- path: "./tests/test-hashing.hs"
component: "test:test-hashing"

- path: "./hello-world.hs"
component: "exe:hello-world"
- path: "./tests/test-hello-world.hs"
component: "test:test-hello-world"

- path: "./if-else.hs"
component: "exe:if-else"
- path: "./tests/test-if-else.hs"
component: "test:test-if-else"

- path: "./invert.hs"
component: "exe:invert"
- path: "./tests/test-invert.hs"
component: "test:test-invert"

- path: "./logging.hs"
component: "exe:logging"
- path: "./tests/test-logging.hs"
component: "test:test-logging"

- path: "./moments-in-time.hs"
component: "exe:moments-in-time"
- path: "./tests/test-moments-in-time.hs"
component: "test:test-moments-in-time"

- path: "./monitoring.hs"
component: "exe:monitoring"
- path: "./tests/test-monitoring.hs"
component: "test:test-monitoring"

- path: "./mutable-references.hs"
component: "exe:mutable-references"
- path: "./tests/test-mutable-references.hs"
component: "test:test-mutable-references"

- path: "./partial-application.hs"
component: "exe:partial-application"
- path: "./tests/test-partial-application.hs"
component: "test:test-partial-application"

- path: "./records-with-optics.hs"
component: "exe:records-with-optics"
- path: "./tests/test-records-with-optics.hs"
component: "test:test-records-with-optics"

- path: "./threads.hs"
component: "exe:threads"
- path: "./tests/test-threads.hs"
component: "test:test-threads"

- path: "./timeouts.hs"
component: "exe:timeouts"
- path: "./tests/test-timeouts.hs"
component: "test:test-timeouts"

- path: "./transactions.hs"
component: "exe:transactions"
- path: "./tests/test-transactions.hs"
component: "test:test-transactions"

- path: "./variables.hs"
component: "exe:variables"
- path: "./tests/test-variables.hs"
component: "test:test-variables"
63 changes: 24 additions & 39 deletions monitoring.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import System.Environment
import System.Exit
import System.IO
import System.Signal
import System.Process

import Control.Concurrent
import Control.Concurrent.Async
Expand Down Expand Up @@ -42,22 +41,24 @@ main =
case args of
["aggregate-reports"] -> aggregateReportsMain
["send-demo-reports"] -> sendDemoReportsMain
["full-demonstration"] -> fullDemonstrationMain
_ -> die "Invalid args"

aggregateReportsMain =
do
reportQueue <- atomically newTQueue
alarmQueue <- atomically newTQueue
withServerSocket $ \serverSocket ->
do
putStrLn "The monitoring server has started."

foldr1 race_
[ receiveReports reportQueue
, analyzeReports reportQueue alarmQueue
, sendAlarms alarmQueue
, waitForTerminationSignal
]
reportQueue <- atomically newTQueue
alarmQueue <- atomically newTQueue

foldr1 race_
[ receiveReports serverSocket reportQueue
, analyzeReports reportQueue alarmQueue
, sendAlarms alarmQueue
, waitForTerminationSignal
]

putStrLn "The monitoring server is stopping."
putStrLn "The monitoring server is stopping."

waitForTerminationSignal =
do
Expand Down Expand Up @@ -91,16 +92,15 @@ withServerSocket action =
S.listen serverSocket S.maxListenQueue
action serverSocket

receiveReports reportQueue =
withServerSocket $ \serverSocket ->
forever $
mask $ \unmask ->
do
(clientSocket, _clientAddr) <- S.accept serverSocket
receiveReports serverSocket reportQueue =
forever $
mask $ \unmask ->
do
(clientSocket, _clientAddr) <- S.accept serverSocket

forkFinally
(unmask (receiveReports' clientSocket reportQueue))
(\_ -> S.close clientSocket)
forkFinally
(unmask (receiveReports' clientSocket reportQueue))
(\_ -> S.close clientSocket)

receiveReports' clientSocket reportQueue = continue
where
Expand All @@ -117,7 +117,9 @@ receiveReports' clientSocket reportQueue = continue

receiveReports'' receivedBytes reportQueue =
for_ @[] (Data.ByteString.Char8.unpack receivedBytes) $ \c ->
for_ @Maybe (decodeReport c) $ \r ->
for_ @Maybe (decodeReport c) $ \r -> do
putStrLn (case r of Success -> "1 (success)"
Failure -> "0 (failure)")
atomically (writeTQueue reportQueue r)


Expand Down Expand Up @@ -176,8 +178,6 @@ sendDemoReportsMain =
, sendReports reportQueue
]

putStrLn "Done sending demo reports."


--- A fixed schedule of event reports for demonstration purposes ---

Expand Down Expand Up @@ -205,20 +205,5 @@ sendReports reportQueue =
forever $
do
r <- atomically (readTQueue reportQueue)
putStrLn (case r of Success -> "1 (success)"
Failure -> "0 (failure)")
sendAll clientSocket
(Data.ByteString.Char8.pack [encodeReport r])


--- Full demonstration ---

fullDemonstrationMain =
do
server <- spawnCommand
"runhaskell monitoring.hs aggregate-reports"
threadDelay 1_000_000
callCommand "runhaskell monitoring.hs send-demo-reports"
terminateProcess server
waitForProcess server
putStrLn "The full demonstration is complete."
20 changes: 0 additions & 20 deletions outputs/bounded-queues.txt

This file was deleted.

3 changes: 0 additions & 3 deletions outputs/branching.txt

This file was deleted.

6 changes: 0 additions & 6 deletions outputs/common-types.txt

This file was deleted.

2 changes: 0 additions & 2 deletions outputs/crypto-hashing.txt

This file was deleted.

3 changes: 0 additions & 3 deletions outputs/dynamic.txt

This file was deleted.

9 changes: 0 additions & 9 deletions outputs/enum-ranges.txt

This file was deleted.

Loading

0 comments on commit 2b0aa44

Please sign in to comment.