Repository: alecthomas/gometalinter
Pull request #288 (2017-06-13 22:52:55)

Use a duration type instead of deadline crutch
This change replaces the `DeadlineJSONCrutch` with a type which handles serializing from a JSON string to a `time.Duration()`. Also adds a test to cover the change.


Patch:
diff --git a/config.go b/config.go
index ac1d4e0..b022d1b 100644
--- a/config.go
+++ b/config.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"encoding/json"
 	"runtime"
 	"text/template"
 	"time"
@@ -45,14 +46,29 @@ type Config struct { // nolint: aligncheck
 	DuplThreshold   int
 	Sort            []string
 	Test            bool
-	Deadline        time.Duration `json:"-"`
+	Deadline        jsonDuration
 	Errors          bool
 	JSON            bool
 	Checkstyle      bool
 	EnableGC        bool
 	Aggregate       bool
+}
+
+type jsonDuration time.Duration
+
+func (td *jsonDuration) UnmarshalJSON(raw []byte) error {
+	var durationAsString string
+	if err := json.Unmarshal(raw, &durationAsString); err != nil {
+		return err
+	}
+	duration, err := time.ParseDuration(durationAsString)
+	*td = jsonDuration(duration)
+	return err
+}
 
-	DeadlineJSONCrutch string `json:"Deadline"`
+// Duration returns the value as a time.Duration
+func (td *jsonDuration) Duration() time.Duration {
+	return time.Duration(*td)
 }
 
 // Configuration defaults.
@@ -183,6 +199,6 @@ var (
 		MinConstLength:  3,
 		DuplThreshold:   50,
 		Sort:            []string{"none"},
-		Deadline:        time.Second * 30,
+		Deadline:        jsonDuration(time.Second * 30),
 	}
 )
diff --git a/main.go b/main.go
index 70d147b..ae13de8 100644
--- a/main.go
+++ b/main.go
@@ -126,6 +126,7 @@ func (s *sortedIssues) Less(i, j int) bool {
 }
 
 func init() {
+	deadline := time.Duration(config.Deadline)
 	kingpin.Flag("config", "Load JSON configuration from file.").Action(loadConfig).String()
 	kingpin.Flag("disable", "Disable previously enabled linters.").PlaceHolder("LINTER").Short('D').Action(disableAction).Strings()
 	kingpin.Flag("enable", "Enable previously disabled linters.").PlaceHolder("LINTER").Short('E').Action(enableAction).Strings()
@@ -155,7 +156,7 @@ func init() {
 	kingpin.Flag("dupl-threshold", "Minimum token sequence as a clone for dupl.").PlaceHolder("50").IntVar(&config.DuplThreshold)
 	kingpin.Flag("sort", fmt.Sprintf("Sort output by any of %s.", strings.Join(sortKeys, ", "))).PlaceHolder("none").EnumsVar(&config.Sort, sortKeys...)
 	kingpin.Flag("tests", "Include test files for linters that support this option").Short('t').BoolVar(&config.Test)
-	kingpin.Flag("deadline", "Cancel linters if they have not completed within this duration.").PlaceHolder("30s").DurationVar(&config.Deadline)
+	kingpin.Flag("deadline", "Cancel linters if they have not completed within this duration.").PlaceHolder("30s").DurationVar(&deadline)
 	kingpin.Flag("errors", "Only show errors.").BoolVar(&config.Errors)
 	kingpin.Flag("json", "Generate structured JSON rather than standard line-based output.").BoolVar(&config.JSON)
 	kingpin.Flag("checkstyle", "Generate checkstyle XML rather than standard line-based output.").BoolVar(&config.Checkstyle)
@@ -174,9 +175,6 @@ func loadConfig(app *kingpin.Application, element *kingpin.ParseElement, ctx *ki
 	if err != nil {
 		return err
 	}
-	if config.DeadlineJSONCrutch != "" {
-		config.Deadline, err = time.ParseDuration(config.DeadlineJSONCrutch)
-	}
 	for _, disable := range config.Disable {
 		for i, enable := range config.Enable {
 			if enable == disable {
@@ -441,7 +439,7 @@ func runLinters(linters map[string]*Linter, paths, ellipsisPaths []string, concu
 		}
 		for _, path := range linterPaths {
 			wg.Add(1)
-			deadline := time.After(config.Deadline)
+			deadline := time.After(config.Deadline.Duration())
 			state := &linterState{
 				Linter:   linter,
 				issues:   incomingIssues,


Test patch:
diff --git a/main_test.go b/main_test.go
index 15fb069..2fcf00a 100644
--- a/main_test.go
+++ b/main_test.go
@@ -1,10 +1,14 @@
 package main
 
 import (
+	"io/ioutil"
 	"sort"
+	"os"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/require"
+	"gopkg.in/alecthomas/kingpin.v3-unstable"
 )
 
 func TestSortedIssues(t *testing.T) {
@@ -27,3 +31,22 @@ func TestSortedIssues(t *testing.T) {
 	}
 	require.Equal(t, expected, actual)
 }
+
+func TestLoadConfigWithDeadline(t *testing.T) {
+	originalConfig := *config
+	defer func() { config = &originalConfig }()
+
+	tmpfile, err := ioutil.TempFile("", "test-config")
+	require.NoError(t, err)
+	defer os.Remove(tmpfile.Name())
+
+	_, err = tmpfile.Write([]byte(`{"Deadline": "3m"}`))
+	require.NoError(t, err)
+	require.NoError(t, tmpfile.Close())
+
+	filename := tmpfile.Name()
+	err = loadConfig(nil, &kingpin.ParseElement{Value: &filename}, nil)
+	require.NoError(t, err)
+
+	require.Equal(t, 3 * time.Minute, config.Deadline.Duration())
+}
\ No newline at end of file