codec-rpm-0.1.0: tests/Codec/RPM/ParseSpec.hs
module Codec.RPM.ParseSpec (spec) where
import Test.Hspec
import Test.Hspec.Attoparsec
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BC
import Codec.RPM.Parse
import Codec.RPM.Tags
import Codec.RPM.Types
spec :: Spec
spec = describe "Codec.RPM.Parse" $ do
describe "parseLead" $ do
it "fails with wrong file signature" $ do
let stream = BS.pack [
0xFF, 0xFF, 0xFF, 0xFF, -- *WRONG* File signature
8, -- Major
9, -- Minor
0, 1, -- Type
0, 2, -- ArchNum
65, 66, 67, 68, 69, 70, -- name 66 bytes ABCDEF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, -- OS num
0, 4, -- Sig type
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -- 16 bytes padding
parseLead `shouldFailOn` stream
-- NOTE: http://alpmestan.com/posts/2014-06-18-testing-attoparsec-parsers-with-hspec.html
-- Right now, hspec-attoparsec will only consider leftovers when the parser succeeds.
-- I’m not really sure whether we should return Fail’s unconsumed input or not.
--
-- We can't use `leavesUnconsumed` when the parser fails
-- so the following assertion is invalid for now!
-- stream ~?> parseLead `leavesUnconsumed` BS.pack []
it "fails when name < 66 chars" $ do
let stream = BS.pack [
0xED, 0xAB, 0xEE, 0xDB, -- File signature
8, -- Major
9, -- Minor
0, 1, -- Type
0, 1, -- ArchNum
65, 66, 67, 68, 69, 70, -- name 26 instead of 66 bytes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, -- OS num
0, 4, -- Sig type
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -- 16 bytes padding
parseLead `shouldFailOn` stream
it "fails when padding < 16 bytes" $ do
let stream = BS.pack [
0xED, 0xAB, 0xEE, 0xDB, -- File signature
8, -- Major
9, -- Minor
0, 1, -- Type
0, 1, -- ArchNum
65, 66, 67, 68, 69, 70, -- name 66 bytes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, -- OS num
0, 4, -- Sig type
0, 0, 0, 0, 0, 0] -- 6 instead of 16 bytes padding
parseLead `shouldFailOn` stream
it "succeeds with valid data" $ do
let stream = BS.pack [
0xED, 0xAB, 0xEE, 0xDB, -- File signature
8, -- Major
9, -- Minor
0, 1, -- Type
0, 2, -- ArchNum
65, 66, 67, 68, 69, 70, -- name 66 bytes ABCDEF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, -- OS num
0, 4, -- Sig type
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -- 16 bytes padding
let expected = Lead 8 9 1 2 "ABCDEF" 1 4
-- parsing succeeds
parseLead `shouldSucceedOn` stream
-- no unconsumed input
stream ~?> parseLead `leavesUnconsumed` BS.pack []
-- result is as expected
stream ~> parseLead `parseSatisfies` (==expected)
describe "parseSectionHeader" $ do
it "fails with invalid section header signature" $ do
let stream = BS.pack [
0xFF, 0xFF, 0xFF, -- *WRONG* section header signature
8, -- sectionVersion
0, 0, 0, 0, -- 4 reserved bytes
255, 255, 255, 255, -- sectionCount 4 bytes
0, 0, 255, 255] -- sectionSize 4 bytes
parseSectionHeader `shouldFailOn` stream
it "fails when reserved section < 4 bytes" $ do
let stream = BS.pack [
0x8E, 0xAD, 0xE8, -- section header signature
8, -- sectionVersion
0, 0, -- *2 instead of* 4 reserved bytes
255, 255, 255, 255, -- sectionCount 4 bytes
0, 0, 255, 255] -- sectionSize 4 bytes
parseSectionHeader `shouldFailOn` stream
it "succeeds with valid data" $ do
let stream = BS.pack [
0x8E, 0xAD, 0xE8, -- section header signature
8, -- sectionVersion
0, 0, 0, 0, -- 4 reserved bytes
255, 255, 255, 255, -- sectionCount 4 bytes
0, 0, 255, 255] -- sectionSize 4 bytes
let expected = SectionHeader 8 4294967295 65535
-- parsing succeeds
parseSectionHeader `shouldSucceedOn` stream
-- no unconsumed input
stream ~?> parseSectionHeader `leavesUnconsumed` BS.pack []
-- result is as expected
stream ~> parseSectionHeader `parseSatisfies` (==expected)
describe "parseOneTag" $ do
it "returns Nothing when any of the input streams is empty" $ do
parseOneTag (BS.pack []) (BS.pack []) `shouldBe` Nothing
parseOneTag (BS.pack [1, 2, 3, 4, 5]) (BS.pack []) `shouldBe` Nothing
parseOneTag (BS.pack []) (BS.pack [100, 8, 0, 7]) `shouldBe` Nothing
it "returns valid Tag when both input streams are valid" $ do
let store = BC.pack "123-test-me"
let bs = BS.pack [0, 0, 0, 100, -- tag
0, 0, 0, 8, -- ty
0, 0, 0, 4, -- offset
0, 0, 0, 7 -- count
]
parseOneTag store bs `shouldBe` Just (HeaderI18NTable ["test-me"])
describe "parseSection" $
it "succeeds with valid data" $ do
let stream = BS.pack [
0x8E, 0xAD, 0xE8, -- section header signature
1, -- sectionVersion
0, 0, 0, 0, -- 4 reserved bytes
0, 0, 0, 7, -- sectionCount 4 bytes
0, 0, 0, 0xE4, -- sectionSize 4 bytes
-- tags defined in this section, sectionCount * 16 bytes
-- 62 7 212 16 == HeaderSignatures (Null), ty(7) /= 0, returns Nothing
0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x10,
-- 267 7 0 72 == DSAHeader (binary)
0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48,
-- 269 6 72 1 == SHA1Header (string)
0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01,
-- 1000 4 116 1 == Name (string), ty(4) /= 6, returns Nothing
0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x01,
-- 1004 7 120 16 == Summary (i18n string), ty(7) /= 9, returns Nothing
0x00, 0x00, 0x03, 0xec, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x10,
-- 1005 7 136 72 == Description (i18n string), ty(7) /= 9, returns Nothing
0x00, 0x00, 0x03, 0xed, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x48,
-- 1007 4 208 1 == BuildHost (string), ty(4) /= 6, returns Nothing
0x00, 0x00, 0x03, 0xef, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x01,
-- section payload (228 bytes in this example)
-- DSAHeader
0x88, 0x46, 0x04, 0x00, 0x11, 0x02, 0x00, 0x06, 0x05, 0x02, 0x53, 0x67, 0xab, 0x9d, 0x00, 0x0a,
0x09, 0x10, 0x50, 0x8c, 0xe5, 0xe6, 0x66, 0x53, 0x4c, 0x2b, 0x6b, 0x83, 0x00, 0xa0, 0x9e, 0x1c,
0x4e, 0x19, 0xd5, 0x78, 0x37, 0x53, 0x61, 0x8a, 0x34, 0x4b, 0x40, 0x91, 0xfb, 0xc1, 0x24, 0xbb,
0x1c, 0x62, 0x00, 0x9f, 0x50, 0xe6, 0x5c, 0x34, 0x72, 0xa6, 0x54, 0x70, 0x45, 0xce, 0xe9, 0xec,
0x02, 0x6b, 0x98, 0xfa, 0x45, 0x72, 0x8f, 0xca, -- SHA1Header
0x66, 0x36, 0x37, 0x35, 0x64, 0x37, 0x39, 0x62,
0x66, 0x66, 0x33, 0x34, 0x34, 0x66, 0x36, 0x63, 0x63, 0x63, 0x32, 0x64, 0x34, 0x65, 0x37, 0x31,
0x66, 0x66, 0x62, 0x62, 0x38, 0x61, 0x39, 0x63, 0x36, 0x38, 0x39, 0x62, 0x61, 0x64, 0x65, 0x63,
0x00, 0x00, 0x00, 0x00, -- Name
0x01, 0x2b, 0x00, 0xf6, -- Summary
0x00, 0x7c, 0xa1, 0x57, 0x31, 0x3e, 0x1f, 0x20,
0x34, 0x4f, 0xf7, 0x1e, 0xc9, 0xbb, 0xd7, 0xdc, -- Description
0x88, 0x46, 0x04, 0x00, 0x11, 0x02, 0x00, 0x06,
0x05, 0x02, 0x53, 0x67, 0xab, 0x9d, 0x00, 0x0a, 0x09, 0x10, 0x50, 0x8c, 0xe5, 0xe6, 0x66, 0x53,
0x4c, 0x2b, 0x6f, 0xdf, 0x00, 0x9d, 0x13, 0x63, 0xe0, 0x2f, 0xed, 0x88, 0x8b, 0x27, 0xad, 0x46,
0x23, 0x26, 0xb7, 0xa8, 0xda, 0xb7, 0xea, 0x64, 0x71, 0x88, 0x00, 0xa0, 0x80, 0x31, 0xf5, 0x32,
0x25, 0x81, 0xf9, 0xce, 0xe1, 0x63, 0x15, 0x15, 0x9d, 0xe3, 0x74, 0xc1, 0x23, 0xa8, 0xba, 0xaf,
-- BuildHost
0x01, 0x2a, 0xeb, 0x70, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x90,
0x00, 0x00, 0x00, 0x00] -- followed by 4 bytes of signature padding before the next section
-- parsing succeeds
parseSection `shouldSucceedOn` stream
-- no unconsumed input
stream ~?> parseSection `leavesUnconsumed` BS.pack []
-- verify the result matches expected
stream ~> parseSection `parseSatisfies` matchExpected
where
matchExpected h = do
let expSH = SectionHeader 1 7 228
let expT = [
DSAHeader (BS.pack [
0x88, 0x46, 0x04, 0x00, 0x11, 0x02, 0x00, 0x06, 0x05, 0x02, 0x53, 0x67, 0xab, 0x9d, 0x00, 0x0a,
0x09, 0x10, 0x50, 0x8c, 0xe5, 0xe6, 0x66, 0x53, 0x4c, 0x2b, 0x6b, 0x83, 0x00, 0xa0, 0x9e, 0x1c,
0x4e, 0x19, 0xd5, 0x78, 0x37, 0x53, 0x61, 0x8a, 0x34, 0x4b, 0x40, 0x91, 0xfb, 0xc1, 0x24, 0xbb,
0x1c, 0x62, 0x00, 0x9f, 0x50, 0xe6, 0x5c, 0x34, 0x72, 0xa6, 0x54, 0x70, 0x45, 0xce, 0xe9, 0xec,
0x02, 0x6b, 0x98, 0xfa, 0x45, 0x72, 0x8f, 0xca]),
SHA1Header "f675d79bff344f6ccc2d4e71ffbb8a9c689badec"
]
headerSectionHeader h == expSH && headerTags h == expT && BS.length (headerStore h) == 228