diff --git a/.travis.yml b/.travis.yml index 519f34b2..9cb674e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,10 +78,7 @@ matrix: # Run the notebook to regenerate the outputs, then compare the new notebook to the old one. stack install --fast stack exec -- ihaskell install --stack - stack exec -- jupyter nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb - - # Images are rendered differently on different systems, so filter them out in the comparison - diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb) + test/acceptance.nbconvert.sh stack exec -- jupyter nbconvert fi - env: DISPLAY=true USE_STACK_YAML="stack-8.2.yaml" # GHC 8.2.2 language: python @@ -158,10 +155,7 @@ matrix: # Run the notebook to regenerate the outputs, then compare the new notebook to the old one. stack install --fast stack exec -- ihaskell install --stack - stack exec -- jupyter nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb - - # Images are rendered differently on different systems, so filter them out in the comparison - diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb) + test/acceptance.nbconvert.sh stack exec -- jupyter nbconvert fi - env: DISPLAY=true USE_STACK_YAML="stack-8.4.yaml" # GHC 8.4.3 language: python @@ -238,10 +232,7 @@ matrix: # Run the notebook to regenerate the outputs, then compare the new notebook to the old one. stack install --fast stack exec -- ihaskell install --stack - stack exec -- jupyter nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb - - # Images are rendered differently on different systems, so filter them out in the comparison - diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb) + test/acceptance.nbconvert.sh stack exec -- jupyter nbconvert fi - env: DISPLAY=false USE_STACK_YAML="stack.yaml" # GHC 8.6.3 language: python @@ -318,10 +309,7 @@ matrix: # Run the notebook to regenerate the outputs, then compare the new notebook to the old one. stack install --fast stack exec -- ihaskell install --stack - stack exec -- jupyter nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb - - # Images are rendered differently on different systems, so filter them out in the comparison - diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb) + test/acceptance.nbconvert.sh stack exec -- jupyter nbconvert fi - language: nix dist: xenial @@ -352,8 +340,7 @@ matrix: ihaskell-plot ihaskell-static-canvas ])' - - result/bin/ihaskell-nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb - - diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb) + - test/acceptance.nbconvert.sh result/bin/ihaskell-nbconvert - language: nix dist: xenial env: RELEASE_NIX="release-8.2.nix" @@ -383,5 +370,4 @@ matrix: ihaskell-plot ihaskell-static-canvas ])' - - result/bin/ihaskell-nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb - - diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb) + - test/acceptance.nbconvert.sh result/bin/ihaskell-nbconvert diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 00000000..cc66d5c1 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,2 @@ +A +acceptance.nbconvert.out.ipynb diff --git a/test/acceptance.nbconvert.in.ipynb b/test/acceptance.nbconvert.in.ipynb new file mode 100644 index 00000000..a280ab97 --- /dev/null +++ b/test/acceptance.nbconvert.in.ipynb @@ -0,0 +1,1155 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "IHaskell Notebook Test 1\n", + "===\n", + "\n", + "Acceptance test for IHaskell, based on `notebooks/IHaskell.ipynb`" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "\"Hello, World!\"" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "-- First of all, we can evaluate simple expressions.\n", + "3 + 5\n", + "\"Hello, \" ++ \"World!\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test the `itN` variable" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Hello, World!\"" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "it1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test multi-line expressions" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Hello, World!\"" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "-- Unlike in GHCi, we can have multi-line expressions.\n", + "concat [\n", + " \"Hello\",\n", + " \", \",\n", + " \"World!\"\n", + " ] :: String" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test top-level declaration" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "12" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "thing :: String -> Int -> Int\n", + "thing \"no\" _ = 100\n", + "thing str int = int + length str\n", + "\n", + "thing \"no\" 10\n", + "thing \"ah\" 10" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test IO" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"What's going on?\"" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print \"What's going on?\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test the `:extension` directive" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "<interactive>:1:1: error:\n • ‘Thing’ has no constructors (EmptyDataDecls permits this)\n • In the data declaration for ‘Thing’" + ] + } + ], + "source": [ + "-- We can disable extensions.\n", + ":ext NoEmptyDataDecls\n", + "data Thing" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "-- And enable extensions.\n", + ":ext EmptyDataDecls\n", + "data Thing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Data declarations" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[A \"Hello\",B 10]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "-- Various data declarations work fine.\n", + "data One\n", + " = A String\n", + " | B Int\n", + " deriving Show\n", + "\n", + "print [A \"Hello\", B 10]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test `:type`" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<style>/* Styles used for the Hoogle display in the pager */\n", + ".hoogle-doc {\n", + "display: block;\n", + "padding-bottom: 1.3em;\n", + "padding-left: 0.4em;\n", + "}\n", + ".hoogle-code {\n", + "display: block;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "}\n", + ".hoogle-text {\n", + "display: block;\n", + "}\n", + ".hoogle-name {\n", + "color: green;\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-head {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-sub {\n", + "display: block;\n", + "margin-left: 0.4em;\n", + "}\n", + ".hoogle-package {\n", + "font-weight: bold;\n", + "font-style: italic;\n", + "}\n", + ".hoogle-module {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-class {\n", + "font-weight: bold;\n", + "}\n", + ".get-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "display: block;\n", + "white-space: pre-wrap;\n", + "}\n", + ".show-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "margin-left: 1em;\n", + "}\n", + ".mono {\n", + "font-family: monospace;\n", + "display: block;\n", + "}\n", + ".err-msg {\n", + "color: red;\n", + "font-style: italic;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "display: block;\n", + "}\n", + "#unshowable {\n", + "color: red;\n", + "font-weight: bold;\n", + "}\n", + ".err-msg.in.collapse {\n", + "padding-top: 0.7em;\n", + "}\n", + ".highlight-code {\n", + "white-space: pre;\n", + "font-family: monospace;\n", + "}\n", + ".suggestion-warning { \n", + "font-weight: bold;\n", + "color: rgb(200, 130, 0);\n", + "}\n", + ".suggestion-error { \n", + "font-weight: bold;\n", + "color: red;\n", + "}\n", + ".suggestion-name {\n", + "font-weight: bold;\n", + "}\n", + "</style><span class='get-type'>3 + 3 :: forall a. Num a => a</span>" + ], + "text/plain": [ + "3 + 3 :: forall a. Num a => a" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "-- We can look at types like in GHCi.\n", + ":ty 3 + 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test `:info`" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + ":opt no-pager" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<style>/* Styles used for the Hoogle display in the pager */\n", + ".hoogle-doc {\n", + "display: block;\n", + "padding-bottom: 1.3em;\n", + "padding-left: 0.4em;\n", + "}\n", + ".hoogle-code {\n", + "display: block;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "}\n", + ".hoogle-text {\n", + "display: block;\n", + "}\n", + ".hoogle-name {\n", + "color: green;\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-head {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-sub {\n", + "display: block;\n", + "margin-left: 0.4em;\n", + "}\n", + ".hoogle-package {\n", + "font-weight: bold;\n", + "font-style: italic;\n", + "}\n", + ".hoogle-module {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-class {\n", + "font-weight: bold;\n", + "}\n", + ".get-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "display: block;\n", + "white-space: pre-wrap;\n", + "}\n", + ".show-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "margin-left: 1em;\n", + "}\n", + ".mono {\n", + "font-family: monospace;\n", + "display: block;\n", + "}\n", + ".err-msg {\n", + "color: red;\n", + "font-style: italic;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "display: block;\n", + "}\n", + "#unshowable {\n", + "color: red;\n", + "font-weight: bold;\n", + "}\n", + ".err-msg.in.collapse {\n", + "padding-top: 0.7em;\n", + "}\n", + ".highlight-code {\n", + "white-space: pre;\n", + "font-family: monospace;\n", + "}\n", + ".suggestion-warning { \n", + "font-weight: bold;\n", + "color: rgb(200, 130, 0);\n", + "}\n", + ".suggestion-error { \n", + "font-weight: bold;\n", + "color: red;\n", + "}\n", + ".suggestion-name {\n", + "font-weight: bold;\n", + "}\n", + "</style><div style='background: rgb(247, 247, 247);'><form><textarea id='code'>class (Real a, Enum a) => Integral a where\n", + " quot :: a -> a -> a\n", + " rem :: a -> a -> a\n", + " div :: a -> a -> a\n", + " mod :: a -> a -> a\n", + " quotRem :: a -> a -> (a, a)\n", + " divMod :: a -> a -> (a, a)\n", + " toInteger :: a -> Integer\n", + " {-# MINIMAL quotRem, toInteger #-}\n", + " \t-- Defined in ‘GHC.Real’\n", + "instance Integral Word -- Defined in ‘GHC.Real’\n", + "instance Integral Integer -- Defined in ‘GHC.Real’\n", + "instance Integral Int -- Defined in ‘GHC.Real’\n", + "</textarea></form></div><script>CodeMirror.fromTextArea(document.getElementById('code'), {mode: 'haskell', readOnly: 'nocursor'});</script>" + ], + "text/plain": [ + "class (Real a, Enum a) => Integral a where\n", + " quot :: a -> a -> a\n", + " rem :: a -> a -> a\n", + " div :: a -> a -> a\n", + " mod :: a -> a -> a\n", + " quotRem :: a -> a -> (a, a)\n", + " divMod :: a -> a -> (a, a)\n", + " toInteger :: a -> Integer\n", + " {-# MINIMAL quotRem, toInteger #-}\n", + " \t-- Defined in ‘GHC.Real’\n", + "instance Integral Word -- Defined in ‘GHC.Real’\n", + "instance Integral Integer -- Defined in ‘GHC.Real’\n", + "instance Integral Int -- Defined in ‘GHC.Real’" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "-- What is the Integral typeclass?\n", + ":info Integral" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test `IHaskellDisplay`" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<style>/* Styles used for the Hoogle display in the pager */\n", + ".hoogle-doc {\n", + "display: block;\n", + "padding-bottom: 1.3em;\n", + "padding-left: 0.4em;\n", + "}\n", + ".hoogle-code {\n", + "display: block;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "}\n", + ".hoogle-text {\n", + "display: block;\n", + "}\n", + ".hoogle-name {\n", + "color: green;\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-head {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-sub {\n", + "display: block;\n", + "margin-left: 0.4em;\n", + "}\n", + ".hoogle-package {\n", + "font-weight: bold;\n", + "font-style: italic;\n", + "}\n", + ".hoogle-module {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-class {\n", + "font-weight: bold;\n", + "}\n", + ".get-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "display: block;\n", + "white-space: pre-wrap;\n", + "}\n", + ".show-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "margin-left: 1em;\n", + "}\n", + ".mono {\n", + "font-family: monospace;\n", + "display: block;\n", + "}\n", + ".err-msg {\n", + "color: red;\n", + "font-style: italic;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "display: block;\n", + "}\n", + "#unshowable {\n", + "color: red;\n", + "font-weight: bold;\n", + "}\n", + ".err-msg.in.collapse {\n", + "padding-top: 0.7em;\n", + "}\n", + ".highlight-code {\n", + "white-space: pre;\n", + "font-family: monospace;\n", + "}\n", + ".suggestion-warning { \n", + "font-weight: bold;\n", + "color: rgb(200, 130, 0);\n", + "}\n", + ".suggestion-error { \n", + "font-weight: bold;\n", + "color: red;\n", + "}\n", + ".suggestion-name {\n", + "font-weight: bold;\n", + "}\n", + "</style><div style='font-weight: bold; color:red'>Look!</div>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<style>/* Styles used for the Hoogle display in the pager */\n", + ".hoogle-doc {\n", + "display: block;\n", + "padding-bottom: 1.3em;\n", + "padding-left: 0.4em;\n", + "}\n", + ".hoogle-code {\n", + "display: block;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "}\n", + ".hoogle-text {\n", + "display: block;\n", + "}\n", + ".hoogle-name {\n", + "color: green;\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-head {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-sub {\n", + "display: block;\n", + "margin-left: 0.4em;\n", + "}\n", + ".hoogle-package {\n", + "font-weight: bold;\n", + "font-style: italic;\n", + "}\n", + ".hoogle-module {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-class {\n", + "font-weight: bold;\n", + "}\n", + ".get-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "display: block;\n", + "white-space: pre-wrap;\n", + "}\n", + ".show-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "margin-left: 1em;\n", + "}\n", + ".mono {\n", + "font-family: monospace;\n", + "display: block;\n", + "}\n", + ".err-msg {\n", + "color: red;\n", + "font-style: italic;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "display: block;\n", + "}\n", + "#unshowable {\n", + "color: red;\n", + "font-weight: bold;\n", + "}\n", + ".err-msg.in.collapse {\n", + "padding-top: 0.7em;\n", + "}\n", + ".highlight-code {\n", + "white-space: pre;\n", + "font-family: monospace;\n", + "}\n", + ".suggestion-warning { \n", + "font-weight: bold;\n", + "color: rgb(200, 130, 0);\n", + "}\n", + ".suggestion-error { \n", + "font-weight: bold;\n", + "color: red;\n", + "}\n", + ".suggestion-name {\n", + "font-weight: bold;\n", + "}\n", + "</style><div style='font-weight: bold; color:green'>Look!</div>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "<style>/* Styles used for the Hoogle display in the pager */\n", + ".hoogle-doc {\n", + "display: block;\n", + "padding-bottom: 1.3em;\n", + "padding-left: 0.4em;\n", + "}\n", + ".hoogle-code {\n", + "display: block;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "}\n", + ".hoogle-text {\n", + "display: block;\n", + "}\n", + ".hoogle-name {\n", + "color: green;\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-head {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-sub {\n", + "display: block;\n", + "margin-left: 0.4em;\n", + "}\n", + ".hoogle-package {\n", + "font-weight: bold;\n", + "font-style: italic;\n", + "}\n", + ".hoogle-module {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-class {\n", + "font-weight: bold;\n", + "}\n", + ".get-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "display: block;\n", + "white-space: pre-wrap;\n", + "}\n", + ".show-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "margin-left: 1em;\n", + "}\n", + ".mono {\n", + "font-family: monospace;\n", + "display: block;\n", + "}\n", + ".err-msg {\n", + "color: red;\n", + "font-style: italic;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "display: block;\n", + "}\n", + "#unshowable {\n", + "color: red;\n", + "font-weight: bold;\n", + "}\n", + ".err-msg.in.collapse {\n", + "padding-top: 0.7em;\n", + "}\n", + ".highlight-code {\n", + "white-space: pre;\n", + "font-family: monospace;\n", + "}\n", + ".suggestion-warning { \n", + "font-weight: bold;\n", + "color: rgb(200, 130, 0);\n", + "}\n", + ".suggestion-error { \n", + "font-weight: bold;\n", + "color: red;\n", + "}\n", + ".suggestion-name {\n", + "font-weight: bold;\n", + "}\n", + "</style><div style='font-weight: bold; color:blue'>Look!</div>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import IHaskell.Display\n", + "\n", + "data Color = Red | Green | Blue\n", + "\n", + "instance IHaskellDisplay Color where\n", + " display color = return $ Display [html code]\n", + " where\n", + " code = concat [\"<div style='font-weight: bold; color:\"\n", + " , css color\n", + " , \"'>Look!</div>\"]\n", + " css Red = \"red\"\n", + " css Blue = \"blue\"\n", + " css Green = \"green\"\n", + "\n", + "Red\n", + "Green\n", + "Blue" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test `hlint` " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<style>/* Styles used for the Hoogle display in the pager */\n", + ".hoogle-doc {\n", + "display: block;\n", + "padding-bottom: 1.3em;\n", + "padding-left: 0.4em;\n", + "}\n", + ".hoogle-code {\n", + "display: block;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "}\n", + ".hoogle-text {\n", + "display: block;\n", + "}\n", + ".hoogle-name {\n", + "color: green;\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-head {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-sub {\n", + "display: block;\n", + "margin-left: 0.4em;\n", + "}\n", + ".hoogle-package {\n", + "font-weight: bold;\n", + "font-style: italic;\n", + "}\n", + ".hoogle-module {\n", + "font-weight: bold;\n", + "}\n", + ".hoogle-class {\n", + "font-weight: bold;\n", + "}\n", + ".get-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "display: block;\n", + "white-space: pre-wrap;\n", + "}\n", + ".show-type {\n", + "color: green;\n", + "font-weight: bold;\n", + "font-family: monospace;\n", + "margin-left: 1em;\n", + "}\n", + ".mono {\n", + "font-family: monospace;\n", + "display: block;\n", + "}\n", + ".err-msg {\n", + "color: red;\n", + "font-style: italic;\n", + "font-family: monospace;\n", + "white-space: pre;\n", + "display: block;\n", + "}\n", + "#unshowable {\n", + "color: red;\n", + "font-weight: bold;\n", + "}\n", + ".err-msg.in.collapse {\n", + "padding-top: 0.7em;\n", + "}\n", + ".highlight-code {\n", + "white-space: pre;\n", + "font-family: monospace;\n", + "}\n", + ".suggestion-warning { \n", + "font-weight: bold;\n", + "color: rgb(200, 130, 0);\n", + "}\n", + ".suggestion-error { \n", + "font-weight: bold;\n", + "color: red;\n", + "}\n", + ".suggestion-name {\n", + "font-weight: bold;\n", + "}\n", + "</style><div class=\"suggestion-name\" style=\"clear:both;\">Redundant $</div><div class=\"suggestion-row\" style=\"float: left;\"><div class=\"suggestion-warning\">Found:</div><div class=\"highlight-code\" id=\"haskell\">f $ 3</div></div><div class=\"suggestion-row\" style=\"float: left;\"><div class=\"suggestion-warning\">Why Not:</div><div class=\"highlight-code\" id=\"haskell\">f 3</div></div><div class=\"suggestion-name\" style=\"clear:both;\">Redundant do</div><div class=\"suggestion-row\" style=\"float: left;\"><div class=\"suggestion-warning\">Found:</div><div class=\"highlight-code\" id=\"haskell\">do return 3</div></div><div class=\"suggestion-row\" style=\"float: left;\"><div class=\"suggestion-warning\">Why Not:</div><div class=\"highlight-code\" id=\"haskell\">return 3</div></div>" + ], + "text/plain": [ + "Line 1: Redundant $\n", + "Found:\n", + "f $ 3\n", + "Why not:\n", + "f 3Line 1: Redundant do\n", + "Found:\n", + "do return 3\n", + "Why not:\n", + "return 3" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "4" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "3" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "-- There is also hlint integration enabled by default.\n", + "-- If you write sketchy code, it will tell you:\n", + "f :: Int -> Int\n", + "f x = x + 1\n", + "\n", + "-- Most warnings are orange...\n", + "f $ 3\n", + "\n", + "do\n", + " return 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test `:help`" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "The following commands are available:\n", + " :extension <Extension> - Enable a GHC extension.\n", + " :extension No<Extension> - Disable a GHC extension.\n", + " :type <expression> - Print expression type.\n", + " :info <name> - Print all info for a name.\n", + " :hoogle <query> - Search for a query on Hoogle.\n", + " :doc <ident> - Get documentation for an identifier via Hogole.\n", + " :set -XFlag -Wall - Set an option (like ghci).\n", + " :option <opt> - Set an option.\n", + " :option no-<opt> - Unset an option.\n", + " :?, :help - Show this help text.\n", + "\n", + "Any prefix of the commands will also suffice, e.g. use :ty for :type.\n", + "\n", + "Options:\n", + " lint – enable or disable linting.\n", + " svg – use svg output (cannot be resized).\n", + " show-types – show types of all bound names\n", + " show-errors – display Show instance missing errors normally.\n", + " pager – use the pager to display results of :info, :doc, :hoogle, etc." + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + ":help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test `module Name where`\n", + "\n", + "IHaskell will create the file `A/B.hs`, compile it, and load it. " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "-- If your code isn't running fast enough, you can just put it into a module.\n", + "module A.B where\n", + "\n", + "fib 0 = 1\n", + "fib 1 = 1\n", + "fib n = fib (n-1) + fib (n-2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test module import\n", + "\n", + "Note that the module is by default imported unqualified, as though you had typed `import A.B`." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "10946" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "10946" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "-- The module is automatically imported unqualified.\n", + "print $ A.B.fib 20\n", + "print $ fib 20" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test module unbind identifiers\n", + "\n", + "since a new module is imported, all previous bound identifiers are now unbound. For instance, we no longer have access to the `f` function from before:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "<interactive>:1:1: error: Variable not in scope: f :: Integer -> t" + ] + } + ], + "source": [ + "f 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test module re-import\n", + "\n", + "re-import this module with another import statement, the original implicit import goes away." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "10946" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "10946" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qualified A.B as Fib\n", + "\n", + "Fib.fib 20\n", + "fib 20" + ] + } + ], + "metadata": { + "hide_input": false, + "kernelspec": { + "display_name": "Haskell", + "language": "haskell", + "name": "haskell" + }, + "language_info": { + "codemirror_mode": "ihaskell", + "file_extension": ".hs", + "name": "haskell", + "pygments_lexer": "Haskell", + "version": "8.6.5" + }, + "latex_envs": { + "bibliofile": "biblio.bib", + "cite_by": "apalike", + "current_citInitial": 1, + "eqLabelWithNumbers": true, + "eqNumInitial": 0 + }, + "nav_menu": {}, + "toc": { + "navigate_menu": true, + "number_sections": true, + "sideBar": true, + "threshold": 6, + "toc_cell": false, + "toc_section_display": "block", + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/test/acceptance.nbconvert.sh b/test/acceptance.nbconvert.sh new file mode 100755 index 00000000..da261c4d --- /dev/null +++ b/test/acceptance.nbconvert.sh @@ -0,0 +1,33 @@ +#! /usr/bin/env bash + +# Run nbconvert acceptance tests +# ------------------------------ +# +# This script must be called from the root directory of IHaskell. +# +# Positional arguments to this script are the invocation for `nbconvert`. +# For example: +# +# Invoke from the the root IHaskell directory: +# +# test/acceptance.nbconvert.sh jupyter nbconvert +# +# Invoke with `stack` from the the root IHaskell directory: +# +# test/acceptance.nbconvert.sh stack exec -- jupyter nbconvert +# +# Invoke with Stack+Docker from the the root IHaskell directory: +# +# test/acceptance.nbconvert.sh stack --docker exec -- jupyter nbconvert +# +# Invoke with Nix from the root IHaskell directory: +# +# test/acceptance.nbconvert.sh result/bin/ihaskell-nbconvert +# + +set -euo pipefail + +$* --to=notebook --execute --allow-errors --stdout test/acceptance.nbconvert.in.ipynb > test/acceptance.nbconvert.out.ipynb + +diff <(grep -v 'version' test/acceptance.nbconvert.in.ipynb) <(grep -v 'version' test/acceptance.nbconvert.out.ipynb) +