{-# LANGUAGE OverloadedStrings #-} module System.CronSpec (spec) where import SpecHelper spec :: Spec spec = sequence_ [describeScheduleMatches, describeCronScheduleShow, describeCrontabEntryShow, describeCrontabShow ] ---- Specs describeScheduleMatches :: Spec describeScheduleMatches = describe "ScheduleMatches" $ do it "matches a catch-all" $ scheduleMatches stars (day 5 25 1 2) it "matches a specific field" $ scheduleMatches stars { hour = Hours (SpecificField 1)} (day 5 25 1 2) it "matches a range" $ scheduleMatches stars { dayOfMonth = DaysOfMonth (RangeField 3 5)} (day 5 4 1 2) it "does not match invalid range" $ not $ scheduleMatches stars { dayOfMonth = DaysOfMonth (RangeField 5 3)} (day 5 4 1 2) it "matches a list" $ scheduleMatches stars { month = Months (ListField [SpecificField 1, SpecificField 2, SpecificField 3])} (day 2 3 1 2) it "matches a step field" $ scheduleMatches stars { dayOfMonth = DaysOfMonth (StepField (RangeField 10 16) 2)} (day 5 12 1 2) it "does not match something missing the step field" $ not $ scheduleMatches stars { dayOfMonth = DaysOfMonth (StepField (RangeField 10 16) 2)} (day 5 13 1 2) it "matches starred stepped fields" $ scheduleMatches stars { minute = Minutes (StepField Star 2)} (day 5 13 1 4) it "does not match fields that miss starred stepped fields" $ not $ scheduleMatches stars { minute = Minutes (StepField Star 2)} (day 5 13 1 5) it "matches multiple fields at once" $ scheduleMatches stars { minute = Minutes (StepField Star 2), dayOfMonth = DaysOfMonth (SpecificField 3), hour = Hours (RangeField 10 14) } (day 5 3 13 2) it "matches a monday as 1" $ scheduleMatches stars { dayOfWeek = DaysOfWeek (SpecificField 1) } (UTCTime (fromGregorian 2014 3 17) 0) it "matches a sunday as 0" $ scheduleMatches stars { dayOfWeek = DaysOfWeek (SpecificField 0) } (UTCTime (fromGregorian 2014 3 16) 0) it "matches a sunday as 7" $ scheduleMatches stars { dayOfWeek = DaysOfWeek (SpecificField 7) } (UTCTime (fromGregorian 2014 3 16) 0) it "matches weekly on a sunday at 0:00" $ scheduleMatches weekly (UTCTime (fromGregorian 2014 4 6) 0) it "does not match weekly on a sunday at some time past midnight" $ not $ scheduleMatches weekly (UTCTime (fromGregorian 2014 6 4) 600) it "does not match weekly on another day at midnight" $ not $ scheduleMatches weekly (UTCTime (fromGregorian 2014 6 5) 600) it "only needs weekday or monthday to match" $ scheduleMatches stars { dayOfWeek = DaysOfWeek (SpecificField 1), dayOfMonth = DaysOfMonth (SpecificField 1) } (UTCTime (fromGregorian 2014 11 1) 600) prop "star matches everything" $ \t -> scheduleMatches stars t prop "exact time matches" $ \t -> let (_, m, d, h, mn) = timeComponents t sched = CronSchedule (Minutes $ SpecificField mn) (Hours $ SpecificField h) (DaysOfMonth $ SpecificField d) (Months $ SpecificField m) (DaysOfWeek Star) in scheduleMatches sched t prop "any time with the same minute as n * * * * matches" $ arbitraryTimeFields $ \y m d h mn -> let sched = stars { minute = Minutes $ SpecificField mn } t = day' y m d h mn in scheduleMatches sched t prop "any time with the diff minute as n * * * * does not match" $ arbitraryTimeFields $ \y m d h mn -> let sched = stars { minute = Minutes $ SpecificField $ stepMax 59 mn } t = day' y m d h mn in not $ scheduleMatches sched t prop "any time with the same hour as * n * * * matches" $ arbitraryTimeFields $ \y m d h mn -> let sched = stars { hour = Hours $ SpecificField h } t = day' y m d h mn in scheduleMatches sched t prop "any time with the diff hour as * n * * * does not match" $ arbitraryTimeFields $ \y m d h mn -> let sched = stars { hour = Hours $ SpecificField $ stepMax 23 h } t = day' y m d h mn in not $ scheduleMatches sched t prop "any time with the same day as * * n * * matches" $ \t -> let (_, m, d, h, mn) = timeComponents t sched = CronSchedule (Minutes $ SpecificField mn) (Hours $ SpecificField h) (DaysOfMonth $ SpecificField d) (Months $ SpecificField m) (DaysOfWeek Star) in scheduleMatches sched t prop "any time with the diff day as * * n * * does not match" $ arbitraryTimeFields $ \y m d h mn -> let sched = stars { dayOfMonth = DaysOfMonth $ SpecificField $ stepMax 31 d } t = day' y m d h mn in not $ scheduleMatches sched t where day = day' 2012 day' y m d h mn = UTCTime (fromGregorian y m d) (diffTime h mn) diffTime h mn = timeOfDayToTime $ TimeOfDay h mn 1 arbitraryTimeFields f y m d h mn = f (getPositive y) (min 12 $ getPositive m) (min 28 $ getPositive d) (min 23 $ getPositive h) (min 59 $ getPositive mn) hoursMins uTime = (hr, mn) where TimeOfDay { todHour = hr, todMin = mn} = timeToTimeOfDay uTime stepMax :: (Enum a, Ord a) => a -> a -> a stepMax mx n | n < mx = succ n | otherwise = pred n describeCronScheduleShow :: Spec describeCronScheduleShow = describe "CronSchedule show" $ do it "formats stars" $ show stars `shouldBe` "CronSchedule * * * * *" it "formats specific numbers" $ show stars { dayOfWeek = DaysOfWeek (SpecificField 3)} `shouldBe` "CronSchedule * * * * 3" it "formats lists" $ show stars { minute = Minutes (ListField [SpecificField 1, SpecificField 2, SpecificField 3])} `shouldBe` "CronSchedule 1,2,3 * * * *" it "formats ranges" $ show stars { hour = Hours (RangeField 7 10)} `shouldBe` "CronSchedule * 7-10 * * *" it "formats steps" $ show stars { dayOfMonth = DaysOfMonth (StepField (ListField [SpecificField 3, SpecificField 5]) 2)} `shouldBe` "CronSchedule * * 3,5/2 * *" it "formats @yearly" $ show yearly `shouldBe` "CronSchedule 0 0 1 1 *" it "formats @monthly" $ show monthly `shouldBe` "CronSchedule 0 0 1 * *" it "formats @weekly" $ show weekly `shouldBe` "CronSchedule 0 0 * * 0" it "formats @daily" $ show daily `shouldBe` "CronSchedule 0 0 * * *" it "formats @hourly" $ show hourly `shouldBe` "CronSchedule 0 * * * *" it "formats everyMinute" $ show everyMinute `shouldBe` "CronSchedule * * * * *" describeCrontabShow :: Spec describeCrontabShow = describe "Crontab Show" $ do it "prints nothing for an empty crontab" $ show (Crontab []) `shouldBe` "" describeCrontabEntryShow :: Spec describeCrontabEntryShow = describe "CrontabEntry Show" $ do it "formats environment variable sets" $ show envSet `shouldBe` "FOO=BAR" it "formats command entries" $ show entry `shouldBe` "* * * * * do stuff" envSet :: CrontabEntry envSet = EnvVariable "FOO" "BAR" entry :: CrontabEntry entry = CommandEntry stars "do stuff" stars :: CronSchedule stars = CronSchedule (Minutes Star) (Hours Star) (DaysOfMonth Star) (Months Star) (DaysOfWeek Star) timeComponents :: UTCTime -> (Integer, Int, Int, Int, Int) timeComponents (UTCTime dy dt) = (y, m, d, h, mn) where (y, m, d) = toGregorian dy (h, mn) = hoursMins dt