Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto-square: Completely rewrite tests. #391

Merged
merged 1 commit into from
Oct 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
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
20 changes: 3 additions & 17 deletions exercises/crypto-square/src/CryptoSquare.hs
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
module CryptoSquare
( ciphertext
, normalizeCiphertext
, normalizePlaintext
, plaintextSegments
) where
module CryptoSquare (encode) where

ciphertext :: String -> String
ciphertext = undefined

normalizeCiphertext :: String -> String
normalizeCiphertext = undefined

normalizePlaintext :: String -> String
normalizePlaintext = undefined

plaintextSegments :: String -> [String]
plaintextSegments = undefined
encode :: String -> String
encode = undefined
32 changes: 12 additions & 20 deletions exercises/crypto-square/src/Example.hs
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
module CryptoSquare ( normalizePlaintext
, plaintextSegments
, ciphertext
, normalizeCiphertext ) where
module CryptoSquare (encode) where

import Data.Char (isAlphaNum, toLower)
import Data.List (transpose)
import Data.Char (isAlphaNum, toLower)
import Data.List (transpose)
import Data.List.Split (chunksOf)

squareSize :: String -> Int
squareSize = ceiling . (sqrt :: Double -> Double) . fromIntegral . length

normalizePlaintext :: String -> String
normalizePlaintext = map toLower . filter isAlphaNum

plaintextSegments :: String -> [String]
plaintextSegments = (squareSize >>= chunksOf) . normalizePlaintext

ciphertext :: String -> String
ciphertext = concat . transpose . plaintextSegments

normalizeCiphertext :: String -> String
normalizeCiphertext = unwords . transpose . plaintextSegments
encode :: String -> String
encode = unwords
. transpose
. (squareSize >>= chunksOf)
. map toLower
. filter isAlphaNum
where
squareSize :: String -> Int
squareSize = ceiling . (sqrt :: Double -> Double) . fromIntegral . length
132 changes: 46 additions & 86 deletions exercises/crypto-square/test/Tests.hs
Original file line number Diff line number Diff line change
@@ -1,93 +1,53 @@
import Test.Hspec (Spec, describe, it, shouldBe)
{-# LANGUAGE RecordWildCards #-}

import Data.Char (isSpace)
import Data.Foldable (for_)
import Data.Function (on)
import Test.Hspec (Spec, describe, it, shouldBe, shouldMatchList)
import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith)

import CryptoSquare
( ciphertext
, normalizeCiphertext
, normalizePlaintext
, plaintextSegments
)
import CryptoSquare (encode)

main :: IO ()
main = hspecWith defaultConfig {configFastFail = True} specs

specs :: Spec
specs = describe "crypto-square" $ do

-- Test cases adapted from `exercism/x-common/crypto-square.json`
-- on 2016-08-02. Some deviations exist and are noted in comments.

describe "normalizePlaintext" $ do

it "Lowercase" $
normalizePlaintext "Hello"
`shouldBe` "hello"

it "Remove spaces" $
normalizePlaintext "Hi there"
`shouldBe` "hithere"

it "Remove punctuation" $
normalizePlaintext "@1, 2%, 3 Go!"
`shouldBe` "123go"

describe "plaintextSegments" $ do

it "empty plaintext results in an empty rectangle" $
plaintextSegments ""
`shouldBe` []

it "4 character plaintext results in an 2x2 rectangle" $
plaintextSegments "Ab Cd"
`shouldBe` [ "ab"
, "cd" ]

it "9 character plaintext results in an 3x3 rectangle" $
plaintextSegments "This is fun!"
`shouldBe` [ "thi"
, "sis"
, "fun" ]

it "54 character plaintext results in an 8x7 rectangle" $
plaintextSegments "If man was meant to stay on the ground, god would have given us roots."
`shouldBe` [ "ifmanwas"
, "meanttos"
, "tayonthe"
, "groundgo"
, "dwouldha"
, "vegivenu"
, "sroots" ]

describe "ciphertext" $ do

-- The function described by the reference file in `x-common`
-- as `encoded` is called `ciphertext` in this track.

it "empty plaintext results in an empty encode" $
ciphertext ""
`shouldBe` ""

it "Non-empty plaintext results in the combined plaintext segments" $
ciphertext "If man was meant to stay on the ground, god would have given us roots."
`shouldBe` "imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau"

describe "normalizeCiphertext" $ do

-- The function described by the reference file in `x-common`
-- as `ciphertext` is called `normalizeCiphertext` in this track.

it "empty plaintext results in an empty ciphertext" $
normalizeCiphertext ""
`shouldBe` ""

it "9 character plaintext results in 3 chunks of 3 characters" $
normalizeCiphertext "This is fun!"
`shouldBe` "tsf hiu isn"

{- In this track the encoded text chunks are not padded with spaces.

it "54 character plaintext results in 7 chunks, the last two padded with spaces" $
normalizeCiphertext "If man was meant to stay on the ground, god would have given us roots."
`shouldBe` "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "

-}
specs = describe "crypto-square" $
describe "encode" $ for_ cases test
where

test Case{..} = describe description $ do

let shouldMatchWords = shouldBe `on` words
shouldMatchString = shouldBe `on` filter (not . isSpace)
shouldMatchChars = shouldMatchList `on` filter (not . isSpace)

it "normalizes the input" $ encode input `shouldMatchChars` expected
it "reorders the characters" $ encode input `shouldMatchString` expected
it "groups the output" $ encode input `shouldMatchWords` expected

-- Test cases created from scratch on 2016-10-05, diverging from `x-common`.

data Case = Case { description :: String
, input :: String
, expected :: String
}

cases :: [Case]
cases = [ Case { description = "perfect square, all lowercase with space"
, input = "a dog"
, expected = "ao dg"
}
, Case { description = "perfect rectangle, mixed case"
, input = "A camel"
, expected = "am ce al"
}
, Case { description = "incomplete square with punctuation"
, input = "Wait, fox!"
, expected = "wtx af io"
}
, Case { description = "incomplete rectangle with symbols"
, input = "cat | cut -d@ -f1 | sort | uniq"
, expected = "ctoi adrq tft c1u usn"
}
]