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
This commit is contained in:
Dan Anglin 2020-02-13 12:42:55 +00:00
parent 5e0cea3626
commit 19eb9b20be
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
13 changed files with 729 additions and 219 deletions

View file

@ -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

View file

@ -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."
_ = notifier.Push(title, text, "", notificator.UR_NORMAL)
func desktopAlert(label, msg string, notifier *notificator.Notificator) {
if len(msg) == 0 {
msg = fmt.Sprintf(defaultNotificationMsgFmt, label)
}
title := fmt.Sprintf("Pominal session: %s", label)
_ = notifier.Push(title, msg, "", notificator.UR_NORMAL)
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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,
},
}

120
config.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}

135
config_cases_test.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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: "",
},
},
},
},
}

54
config_test.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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)
}
}
}

22
const_test.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
package main
const success = "\u2713"
const failure = "\u2717"

View file

@ -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
}

43
main.go
View file

@ -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)

View file

@ -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{
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: setMaxWorkSessions(m),
maxWorkSessions: maxWorkSessions,
cycle: initCycle,
work: setSessionTime(w),
shortBreak: setSessionTime(s),
longBreak: setSessionTime(l),
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
}
// 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.
sTimeInSecs := sTime.Seconds()
if sTimeInSecs < minimumSessionTime {
sTimeInSecs = minimumSessionTime
}
return sTimeInSecs, nil
}
// 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

237
pominal_cases_test.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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,
},
}

View file

@ -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

17
testdata/pominal.json vendored Normal file
View file

@ -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
}