From 19eb9b20be93bb46f298fe0a1b84155deb75dc3f Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Thu, 13 Feb 2020 12:42:55 +0000 Subject: [PATCH] feat: support configuring Pominal with config file This commit adds a feature to load Pominal configuration from a JSON configuration file. A new data type called PominalConfig was created for the purpose of configuring Pominal. A new factory function called newPominalConfig was created which takes the path to the JSON configuration file and the flag overrides and generates the new PominalConfig value. Here, the JSON config is parsed to create the initial configuration and any flag overrides can override the corresponding fields in the PominalConfig object. Currently only the sessions times and the maximum work sessions per Pominal cycle have flag overrides. Additionally users can now configure custom notification messages for each session type from the configuration file. There are currently no flag overrides for these. This commit resolves dananglin/Pominal#1 --- .gitlab-ci.yml | 2 +- alert.go | 11 +- cases_test.go | 150 ---------------------- config.go | 120 ++++++++++++++++++ config_cases_test.go | 135 ++++++++++++++++++++ config_test.go | 54 ++++++++ const_test.go | 22 ++++ examples/config/pominal.json | 17 +++ main.go | 43 +++---- pominal.go | 112 ++++++++++++----- pominal_cases_test.go | 237 +++++++++++++++++++++++++++++++++++ pominal_test.go | 28 +++-- testdata/pominal.json | 17 +++ 13 files changed, 729 insertions(+), 219 deletions(-) delete mode 100644 cases_test.go create mode 100644 config.go create mode 100644 config_cases_test.go create mode 100644 config_test.go create mode 100644 const_test.go create mode 100644 examples/config/pominal.json create mode 100644 pominal_cases_test.go create mode 100644 testdata/pominal.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 146c729..9d6371a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,7 +16,7 @@ test:unit: paths: - code-coverage.html extends: .install-make - image: golang:1.13.6-alpine + image: golang:1.13.7-alpine script: - make test_cover_report stage: test diff --git a/alert.go b/alert.go index 1771313..65dec84 100644 --- a/alert.go +++ b/alert.go @@ -55,9 +55,12 @@ func getNotificationIconPath() string { } // alert creates a new desktop notification. -func desktopAlert(l string, notifier *notificator.Notificator) { - title := "Pominal notification." - text := l + " session has started." +func desktopAlert(label, msg string, notifier *notificator.Notificator) { + if len(msg) == 0 { + msg = fmt.Sprintf(defaultNotificationMsgFmt, label) + } - _ = notifier.Push(title, text, "", notificator.UR_NORMAL) + title := fmt.Sprintf("Pominal session: %s", label) + + _ = notifier.Push(title, msg, "", notificator.UR_NORMAL) } diff --git a/cases_test.go b/cases_test.go deleted file mode 100644 index 729b33c..0000000 --- a/cases_test.go +++ /dev/null @@ -1,150 +0,0 @@ -/* - Pominal - Copyright (C) 2020 Daniel Anglin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package main - -var newPominalTestCases = []struct { - name string - description string - workSession float64 - shortBreakSession float64 - longBreakSession float64 - maxWorkSessions int - expectedPominal Pominal -}{ - { - name: "Test case 1", - description: "When creating a Pominal value with all valid parameters.", - workSession: 1200, - shortBreakSession: 300, - longBreakSession: 600, - maxWorkSessions: 5, - expectedPominal: Pominal{ - workSession: 1, - maxWorkSessions: 5, - cycle: 1, - countdown: 0, - work: 1200, - shortBreak: 300, - longBreak: 600, - label: "", - stopChan: nil, - }, - }, - { - name: "Test case 2", - description: "When creating a Pominal value where the work session is set below the minimum allowed time.", - workSession: 59.9999999999999999999999999999999, - shortBreakSession: 1536, - longBreakSession: 1200, - maxWorkSessions: 4, - expectedPominal: Pominal{ - workSession: 1, - maxWorkSessions: 4, - cycle: 1, - countdown: 0, - work: 60, - shortBreak: 1536, - longBreak: 1200, - label: "", - stopChan: nil, - }, - }, - { - name: "Test case 3", - description: "When creating a Pominal value where both short break and long break sessions are set below the minimum allowed time.", - workSession: 1000, - shortBreakSession: 1.23, - longBreakSession: 45.6743, - maxWorkSessions: 1, - expectedPominal: Pominal{ - workSession: 1, - maxWorkSessions: 1, - cycle: 1, - countdown: 0, - work: 1000, - shortBreak: 60, - longBreak: 60, - label: "", - stopChan: nil, - }, - }, - { - name: "Test case 4", - description: "When creatng a Pominal where the maximum work sessions set below 1.", - workSession: 1500, - shortBreakSession: 300, - longBreakSession: 1200, - maxWorkSessions: -1, - expectedPominal: Pominal{ - workSession: 1, - maxWorkSessions: 1, - cycle: 1, - countdown: 0, - work: 1500, - shortBreak: 300, - longBreak: 1200, - label: "", - stopChan: nil, - }, - }, -} - -var sessionsTestCases = []struct { - name string - description string - expectedWorkSession int - expectedCycle int - expectedLabel string -}{ - { - name: "Test session 1", - description: "When the first work session has started.", - expectedWorkSession: 1, - expectedCycle: 1, - expectedLabel: workTimerLabel, - }, - { - name: "Test session 2", - description: "When the first work session has finished and the short break session has started.", - expectedWorkSession: 1, - expectedCycle: 1, - expectedLabel: shortBreakTimerLabel, - }, - { - name: "Test session 3", - description: "When the short break session has finished and the second work session has started.", - expectedWorkSession: 2, - expectedCycle: 1, - expectedLabel: workTimerLabel, - }, - { - name: "Test session 4", - description: "When the second work session has finished and the long break has started.", - expectedWorkSession: 2, - expectedCycle: 1, - expectedLabel: longBreakTimerLabel, - }, - { - name: "Test session 5", - description: "When the long break session has finished and the new work session has started for the next Pominal cycle.", - expectedWorkSession: 1, - expectedCycle: 2, - expectedLabel: workTimerLabel, - }, -} diff --git a/config.go b/config.go new file mode 100644 index 0000000..452fd80 --- /dev/null +++ b/config.go @@ -0,0 +1,120 @@ +/* + Pominal + Copyright (C) 2020 Daniel Anglin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package main + +import ( + "encoding/json" + "os" +) + +// Pominal config is a type used to configure Pominal +type PominalConfig struct { + MaxWorkSessions int `json:"max-work-sessions"` + Sessions Sessions `json:"sessions"` +} + +type Sessions struct { + Work SessionConfig `json:"work"` + ShortBreak SessionConfig `json:"short-break"` + LongBreak SessionConfig `json:"long-break"` +} + +type SessionConfig struct { + Duration string `json:"duration"` + NotificationMessage string `json:"notification-message"` +} + +// newPominalConfig creates a new value of type config which will configure +// Pominal based on the configuration set in the configuration file +// and the command-line flags. +func newPominalConfig(file string, workTime, shortBreakTime, longBreakTime string, maxWorkSessions int) (PominalConfig, error) { + c, err := configureFromFile(file) + if err != nil { + return PominalConfig{}, err + } + + c = configureFlagOverrides(c, workTime, shortBreakTime, longBreakTime, maxWorkSessions) + + return c, nil +} + +// configureFromFile parses the JSON file and create the initial +// config value. If the argument is an empty string (i.e. the user +// has not specified the path to a configuration file) a new config value will +// be created from the default values. +func configureFromFile(file string) (PominalConfig, error) { + if len(file) == 0 { + config := PominalConfig{ + MaxWorkSessions: defaultMaxWorkSessions, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: defaultWorkTime, + }, + ShortBreak: SessionConfig{ + Duration: defaultShortBreakTime, + }, + LongBreak: SessionConfig{ + Duration: defaultLongBreakTime, + }, + }, + } + + return config, nil + } + + f, err := os.Open(file) + if err != nil { + return PominalConfig{}, err + } + + defer f.Close() + + dec := json.NewDecoder(f) + config := PominalConfig{} + + if err := dec.Decode(&config); err != nil { + return PominalConfig{}, err + } + + return config, nil +} + +// configureFlagOverrides returns the config value where the +// command-line flags override the corresponding fields in the +// config value. Right now the session durations and the maximum +// work sessions can be overridden. +func configureFlagOverrides(config PominalConfig, workTime, shortBreakTime, longBreakTime string, maxWorkSessions int) PominalConfig { + if len(workTime) > 0 { + config.Sessions.Work.Duration = workTime + } + + if len(shortBreakTime) > 0 { + config.Sessions.ShortBreak.Duration = shortBreakTime + } + + if len(longBreakTime) > 0 { + config.Sessions.LongBreak.Duration = longBreakTime + } + + if maxWorkSessions > 0 { + config.MaxWorkSessions = maxWorkSessions + } + + return config +} diff --git a/config_cases_test.go b/config_cases_test.go new file mode 100644 index 0000000..d58730a --- /dev/null +++ b/config_cases_test.go @@ -0,0 +1,135 @@ +/* + Pominal + Copyright (C) 2020 Daniel Anglin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package main + +var configTestCases = []struct { + name string + description string + file string + workTimeFlag string + shortBreakTimeFlag string + longBreakTimeFlag string + maxWorkSessionsFlag int + expectedConfiguration PominalConfig +}{ + { + name: "Test case 1", + description: "When the configuration file and flag overrides are not set.", + file: "", + workTimeFlag: "", + shortBreakTimeFlag: "", + longBreakTimeFlag: "", + maxWorkSessionsFlag: 0, + expectedConfiguration: PominalConfig{ + MaxWorkSessions: defaultMaxWorkSessions, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: defaultWorkTime, + NotificationMessage: "", + }, + ShortBreak: SessionConfig{ + Duration: defaultShortBreakTime, + NotificationMessage: "", + }, + LongBreak: SessionConfig{ + Duration: defaultLongBreakTime, + NotificationMessage: "", + }, + }, + }, + }, + { + name: "Test case 2", + description: "When the configuration file is set and the flag overrides are not set.", + file: "./testdata/pominal.json", + workTimeFlag: "", + shortBreakTimeFlag: "", + longBreakTimeFlag: "", + maxWorkSessionsFlag: 0, + expectedConfiguration: PominalConfig{ + MaxWorkSessions: 2, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: "30m", + NotificationMessage: "Time for work!", + }, + ShortBreak: SessionConfig{ + Duration: "10m", + NotificationMessage: "Take a short break.", + }, + LongBreak: SessionConfig{ + Duration: "30m", + NotificationMessage: "Long break time! Put the kettle on.", + }, + }, + }, + }, + { + name: "Test case 3", + description: "When the configuration file is set and some flags overrides are set.", + file: "./testdata/pominal.json", + workTimeFlag: "100m", + shortBreakTimeFlag: "", + longBreakTimeFlag: "", + maxWorkSessionsFlag: 6, + expectedConfiguration: PominalConfig{ + MaxWorkSessions: 6, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: "100m", + NotificationMessage: "Time for work!", + }, + ShortBreak: SessionConfig{ + Duration: "10m", + NotificationMessage: "Take a short break.", + }, + LongBreak: SessionConfig{ + Duration: "30m", + NotificationMessage: "Long break time! Put the kettle on.", + }, + }, + }, + }, + { + name: "Test case 4", + description: "When the configuration file is not set and all the flag overrides are set.", + file: "", + workTimeFlag: "100m", + shortBreakTimeFlag: "5m", + longBreakTimeFlag: "60m", + maxWorkSessionsFlag: 3, + expectedConfiguration: PominalConfig{ + MaxWorkSessions: 3, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: "100m", + NotificationMessage: "", + }, + ShortBreak: SessionConfig{ + Duration: "5m", + NotificationMessage: "", + }, + LongBreak: SessionConfig{ + Duration: "60m", + NotificationMessage: "", + }, + }, + }, + }, +} diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..54fa1b2 --- /dev/null +++ b/config_test.go @@ -0,0 +1,54 @@ +/* + Pominal + Copyright (C) 2020 Daniel Anglin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package main + +import ( + "reflect" + "testing" +) + +// TestNewConfig validates the factory function for generating new +// PominalConfig values. +func TestNewPominalConfig(t *testing.T) { + t.Log("Given the need to test the creation of a new Config.") + { + for _, c := range configTestCases { + testFunc := func(t *testing.T) { + t.Log(c.description) + res, err := newPominalConfig( + c.file, + c.workTimeFlag, + c.shortBreakTimeFlag, + c.longBreakTimeFlag, + c.maxWorkSessionsFlag, + ) + if err != nil { + t.Fatalf("%s\tError while creating the PominalConfig value, %s", failure, err.Error()) + } + + if !reflect.DeepEqual(res, c.expectedConfiguration) { + t.Errorf("%s\tUnexpected PominalConfig value created. Expected %v, received %v.", failure, c.expectedConfiguration, res) + } else { + t.Logf("%s\tTest passed.", success) + } + } + t.Run(c.name, testFunc) + } + } +} diff --git a/const_test.go b/const_test.go new file mode 100644 index 0000000..2873a50 --- /dev/null +++ b/const_test.go @@ -0,0 +1,22 @@ +/* + Pominal + Copyright (C) 2020 Daniel Anglin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package main + +const success = "\u2713" +const failure = "\u2717" diff --git a/examples/config/pominal.json b/examples/config/pominal.json new file mode 100644 index 0000000..cf52cc7 --- /dev/null +++ b/examples/config/pominal.json @@ -0,0 +1,17 @@ +{ + "sessions": { + "work": { + "duration": "25m", + "notification-message": "Time for work!" + }, + "short-break": { + "duration": "5m", + "notification-message": "Take a short break." + }, + "long-break": { + "duration": "20m", + "notification-message": "Long break time! Put the kettle on." + } + }, + "max-work-sessions": 4 +} diff --git a/main.go b/main.go index f68401d..a09a092 100644 --- a/main.go +++ b/main.go @@ -22,11 +22,22 @@ import ( "flag" "log" "os" - "time" "github.com/rivo/tview" ) +const ( + workTimerLabel string = "Work" + shortBreakTimerLabel string = "Short break" + longBreakTimerLabel string = "Long break" + defaultWorkTime string = "25m" + defaultShortBreakTime string = "5m" + defaultLongBreakTime string = "20m" + defaultMaxWorkSessions int = 4 + defaultNotificationMsgFmt string = "%s session is now running." + minimumSessionTime float64 = 60 +) + func main() { var ( workTime string @@ -34,13 +45,15 @@ func main() { longBreakTime string maxWorkSessions int printVersion bool + configFilePath string ) - flag.StringVar(&workTime, "work", "25m", "sets the timer for your work session.") - flag.StringVar(&shortBreakTime, "short-break", "5m", "sets the timer for your short break.") - flag.StringVar(&longBreakTime, "long-break", "20m", "sets the timer for your long break.") - flag.IntVar(&maxWorkSessions, "max-work-sessions", 4, "sets the maximum number of work cycles to complete before taking a long break.") + flag.StringVar(&workTime, "work", "", "sets the timer for your work session.") + flag.StringVar(&shortBreakTime, "short-break", "", "sets the timer for your short break.") + flag.StringVar(&longBreakTime, "long-break", "", "sets the timer for your long break.") + flag.IntVar(&maxWorkSessions, "max-work-sessions", 0, "sets the maximum number of work cycles to complete before taking a long break.") flag.BoolVar(&printVersion, "version", false, "print version and exit.") + flag.StringVar(&configFilePath, "config", "", "the path to your Pominal configuration file.") flag.Parse() if printVersion { @@ -48,28 +61,16 @@ func main() { os.Exit(0) } - w, err := time.ParseDuration(workTime) + config, err := newPominalConfig(configFilePath, workTime, shortBreakTime, longBreakTime, maxWorkSessions) if err != nil { - log.Fatalf("ERROR: Unable to set the work timer. %s", err.Error()) + log.Fatalf("ERROR: Unable to create the Pominal configuration. %s", err.Error()) } - s, err := time.ParseDuration(shortBreakTime) + pominal, err := newPominal(config) if err != nil { - log.Fatalf("ERROR: Unable to set the work timer. %s", err.Error()) + log.Fatalf("ERROR: Unable to create Pominal. %s", err.Error()) } - l, err := time.ParseDuration(longBreakTime) - if err != nil { - log.Fatalf("ERROR: Unable to set the work timer. %s", err.Error()) - } - - pominal := NewPominal( - w.Seconds(), - s.Seconds(), - l.Seconds(), - maxWorkSessions, - ) - app := tview.NewApplication() infoUI := newInfoUI() timerUI := newTimerUI(app) diff --git a/pominal.go b/pominal.go index df39aaf..b756d46 100644 --- a/pominal.go +++ b/pominal.go @@ -24,12 +24,6 @@ import ( "github.com/rivo/tview" ) -const ( - workTimerLabel string = "Work" - shortBreakTimerLabel string = "Short break" - longBreakTimerLabel string = "Long break" -) - type Pominal struct { // workSession is a counter for work sessions. workSession int @@ -56,6 +50,23 @@ type Pominal struct { //for the long break sessions. longBreak float64 + // workNotificationMsg is the desktop notification + // message when a work session has started. + workNotificationMsg string + + // shortBreakNotificationMsg is the desktop notification + // message when a short break session has started. + shortBreakNotificationMsg string + + // longBreakNotificationMsg is the desktop notification + // message when a long break session has started. + longBreakNotificationMsg string + + // currentNotificationMsg is used for desktop notifications. + // This will be equal to workNotificationMsg, shortBreakNotificationMsg + // or longBreakNotificationMsg depending on the session in progress. + currentNotificationMsg string + // label labels Pominal based on the session. label string @@ -64,19 +75,41 @@ type Pominal struct { } // NewPominal creates a new pominal instance -func NewPominal(w, s, l float64, m int) Pominal { +func newPominal(config PominalConfig) (Pominal, error) { initWorkSession := 1 initCycle := 1 - return Pominal{ - workSession: initWorkSession, - maxWorkSessions: setMaxWorkSessions(m), - cycle: initCycle, - work: setSessionTime(w), - shortBreak: setSessionTime(s), - longBreak: setSessionTime(l), - stopChan: nil, + maxWorkSessions := setMaxWorkSessions(config.MaxWorkSessions) + + work, err := setSessionTime(config.Sessions.Work.Duration, defaultWorkTime) + if err != nil { + return Pominal{}, err } + + shortBreak, err := setSessionTime(config.Sessions.ShortBreak.Duration, defaultShortBreakTime) + if err != nil { + return Pominal{}, err + } + + longBreak, err := setSessionTime(config.Sessions.LongBreak.Duration, defaultLongBreakTime) + if err != nil { + return Pominal{}, err + } + + p := Pominal{ + workSession: initWorkSession, + maxWorkSessions: maxWorkSessions, + cycle: initCycle, + work: work, + shortBreak: shortBreak, + longBreak: longBreak, + workNotificationMsg: config.Sessions.Work.NotificationMessage, + shortBreakNotificationMsg: config.Sessions.ShortBreak.NotificationMessage, + longBreakNotificationMsg: config.Sessions.LongBreak.NotificationMessage, + stopChan: nil, + } + + return p, nil } // Run Pominal @@ -108,7 +141,7 @@ infinite: p.maxWorkSessions, p.label, ) - desktopAlert(p.label, notifier) + desktopAlert(p.label, p.currentNotificationMsg, notifier) time.Sleep(time.Second) t = time.NewTicker(time.Second) } else { @@ -136,45 +169,62 @@ func (p *Pominal) UpdateSession() { if p.workSession >= p.maxWorkSessions { p.countdown = p.longBreak p.label = longBreakTimerLabel + p.currentNotificationMsg = p.longBreakNotificationMsg } else { p.countdown = p.shortBreak p.label = shortBreakTimerLabel + p.currentNotificationMsg = p.shortBreakNotificationMsg } case shortBreakTimerLabel: p.workSession++ p.countdown = p.work p.label = workTimerLabel + p.currentNotificationMsg = p.workNotificationMsg case longBreakTimerLabel: p.cycle++ p.workSession = 1 p.countdown = p.work p.label = workTimerLabel + p.currentNotificationMsg = p.workNotificationMsg default: p.countdown = p.work p.label = workTimerLabel + p.currentNotificationMsg = p.workNotificationMsg } } -// setSessionTime returns the minimum session time -// if the value is less than the minimum. -// Otherwise it returns the value. -func setSessionTime(s float64) float64 { - var min float64 = 60 - if s < min { - return min +// setSessionTime parses the configured session time and +// returns the session time represented in seconds. +// If the session time is unconfigured then the default for +// that particular session is returned. +// If the value is less than the minimum session time +// then the minimum is returned instead. +// An error is returned if the configured session time cannot be parsed. +func setSessionTime(s, defaultSessionTime string) (float64, error) { + if len(s) == 0 { + s = defaultSessionTime } - return s + sTime, err := time.ParseDuration(s) + if err != nil { + return float64(0), err + } + + sTimeInSecs := sTime.Seconds() + if sTimeInSecs < minimumSessionTime { + sTimeInSecs = minimumSessionTime + } + + return sTimeInSecs, nil } -// setMaxWorkSessions returns the minimum amount -// of work session per Pominal cycle if the user supplies -// a value below it. -// Otherwise it returns the value set by the user. +// setMaxWorkSessions checks and returns the configured +// number of work session per Pominal cycle. +// The default value is returned if the argument is 0 +// or less. func setMaxWorkSessions(w int) int { - var min = 1 - if w < min { - return min + if w <= 0 { + w = defaultMaxWorkSessions } return w diff --git a/pominal_cases_test.go b/pominal_cases_test.go new file mode 100644 index 0000000..b708eab --- /dev/null +++ b/pominal_cases_test.go @@ -0,0 +1,237 @@ +/* + Pominal + Copyright (C) 2020 Daniel Anglin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package main + +var newPominalTestCases = []struct { + name string + description string + config PominalConfig + expectedPominal Pominal +}{ + { + name: "Test case 1", + description: "When creating a Pominal value with all valid parameters.", + config: PominalConfig{ + MaxWorkSessions: 5, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: "20m", + NotificationMessage: "work time!", + }, + ShortBreak: SessionConfig{ + Duration: "5m", + NotificationMessage: "break time!", + }, + LongBreak: SessionConfig{ + Duration: "10m", + NotificationMessage: "long break time!", + }, + }, + }, + expectedPominal: Pominal{ + workSession: 1, + maxWorkSessions: 5, + cycle: 1, + countdown: 0, + work: 1200, + shortBreak: 300, + longBreak: 600, + workNotificationMsg: "work time!", + shortBreakNotificationMsg: "break time!", + longBreakNotificationMsg: "long break time!", + currentNotificationMsg: "", + label: "", + stopChan: nil, + }, + }, + { + name: "Test case 2", + description: "When creating a Pominal value where the work session is set below the minimum allowed time.", + config: PominalConfig{ + MaxWorkSessions: 4, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: "59s999ms", + }, + ShortBreak: SessionConfig{ + Duration: "25m36s", + }, + LongBreak: SessionConfig{ + Duration: "30m", + }, + }, + }, + expectedPominal: Pominal{ + workSession: 1, + maxWorkSessions: 4, + cycle: 1, + countdown: 0, + work: 60, + shortBreak: 1536, + longBreak: 1800, + workNotificationMsg: "", + shortBreakNotificationMsg: "", + longBreakNotificationMsg: "", + currentNotificationMsg: "", + label: "", + stopChan: nil, + }, + }, + { + name: "Test case 3", + description: "When creating a Pominal value where both short break and long break sessions are set below the minimum allowed time.", + config: PominalConfig{ + MaxWorkSessions: 1, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: "16m40s", + }, + ShortBreak: SessionConfig{ + Duration: "1s23ms", + }, + LongBreak: SessionConfig{ + Duration: "45s674ms", + }, + }, + }, + expectedPominal: Pominal{ + workSession: 1, + maxWorkSessions: 1, + cycle: 1, + countdown: 0, + work: 1000, + shortBreak: 60, + longBreak: 60, + workNotificationMsg: "", + shortBreakNotificationMsg: "", + longBreakNotificationMsg: "", + currentNotificationMsg: "", + label: "", + stopChan: nil, + }, + }, + { + name: "Test case 4", + description: "When creatng a Pominal where the maximum work sessions set below 1 (this covers unconfigured).", + config: PominalConfig{ + MaxWorkSessions: -1, + Sessions: Sessions{ + Work: SessionConfig{ + Duration: "25m", + }, + ShortBreak: SessionConfig{ + Duration: "5m", + }, + LongBreak: SessionConfig{ + Duration: "20m", + }, + }, + }, + expectedPominal: Pominal{ + workSession: 1, + maxWorkSessions: defaultMaxWorkSessions, + cycle: 1, + countdown: 0, + work: 1500, + shortBreak: 300, + longBreak: 1200, + workNotificationMsg: "", + shortBreakNotificationMsg: "", + longBreakNotificationMsg: "", + currentNotificationMsg: "", + label: "", + stopChan: nil, + }, + }, + { + name: "Test case 5", + description: "When creatng a Pominal where the work session time is unconfigured.", + config: PominalConfig{ + MaxWorkSessions: 4, + Sessions: Sessions{ + Work: SessionConfig{}, + ShortBreak: SessionConfig{ + Duration: "10m", + }, + LongBreak: SessionConfig{ + Duration: "30m", + }, + }, + }, + expectedPominal: Pominal{ + workSession: 1, + maxWorkSessions: 4, + cycle: 1, + countdown: 0, + work: 1500, + shortBreak: 600, + longBreak: 1800, + workNotificationMsg: "", + shortBreakNotificationMsg: "", + longBreakNotificationMsg: "", + currentNotificationMsg: "", + label: "", + stopChan: nil, + }, + }, +} + +var sessionsTestCases = []struct { + name string + description string + expectedWorkSession int + expectedCycle int + expectedLabel string +}{ + { + name: "Test session 1", + description: "When the first work session has started.", + expectedWorkSession: 1, + expectedCycle: 1, + expectedLabel: workTimerLabel, + }, + { + name: "Test session 2", + description: "When the first work session has finished and the short break session has started.", + expectedWorkSession: 1, + expectedCycle: 1, + expectedLabel: shortBreakTimerLabel, + }, + { + name: "Test session 3", + description: "When the short break session has finished and the second work session has started.", + expectedWorkSession: 2, + expectedCycle: 1, + expectedLabel: workTimerLabel, + }, + { + name: "Test session 4", + description: "When the second work session has finished and the long break has started.", + expectedWorkSession: 2, + expectedCycle: 1, + expectedLabel: longBreakTimerLabel, + }, + { + name: "Test session 5", + description: "When the long break session has finished and the new work session has started for the next Pominal cycle.", + expectedWorkSession: 1, + expectedCycle: 2, + expectedLabel: workTimerLabel, + }, +} diff --git a/pominal_test.go b/pominal_test.go index 93b3d4b..420d762 100644 --- a/pominal_test.go +++ b/pominal_test.go @@ -26,25 +26,20 @@ import ( "github.com/rivo/tview" ) -const success = "\u2713" -const failure = "\u2717" - // TestNewPominal validates the factory function for generating new -// Pominal values +// Pominal values from PominalConfig values. func TestNewPominal(t *testing.T) { t.Log("Given the need to test the creation of a new Pominal instance.") { for _, p := range newPominalTestCases { testFunc := func(t *testing.T) { t.Log(p.description) - res := NewPominal( - p.workSession, - p.shortBreakSession, - p.longBreakSession, - p.maxWorkSessions, - ) + res, err := newPominal(p.config) + if err != nil { + t.Fatalf("%s\tError while creating the Pominal value, %s", failure, err.Error()) + } if !reflect.DeepEqual(res, p.expectedPominal) { - t.Errorf("%s\tUnexpected Pominal created. Expected %v, received %v.", failure, p.expectedPominal, res) + t.Errorf("%s\tUnexpected Pominal value created. Expected %v, received %v.", failure, p.expectedPominal, res) } else { t.Logf("%s\tTest passed.", success) } @@ -63,7 +58,16 @@ func TestNewPominal(t *testing.T) { // are set for each session. This test runs for exactly one // pominal cycle. func TestRun(t *testing.T) { - pominal := NewPominal(60, 60, 60, 2) + config, err := newPominalConfig("", "1m", "1m", "1m", 2) + if err != nil { + t.Fatalf("%s\tError while creating the PominalConfig value, %s", failure, err.Error()) + } + + pominal, err := newPominal(config) + if err != nil { + t.Fatalf("%s\tError while creating the Pominal value, %s", failure, err.Error()) + } + defer pominal.Stop() // create the most basic text view for the info and timer diff --git a/testdata/pominal.json b/testdata/pominal.json new file mode 100644 index 0000000..4b867fd --- /dev/null +++ b/testdata/pominal.json @@ -0,0 +1,17 @@ +{ + "sessions": { + "work": { + "duration": "30m", + "notification-message": "Time for work!" + }, + "short-break": { + "duration": "10m", + "notification-message": "Take a short break." + }, + "long-break": { + "duration": "30m", + "notification-message": "Long break time! Put the kettle on." + } + }, + "max-work-sessions": 2 +}