1// Copyright (c) 2017, OpenPeeDeeP. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package xdg
6
7import (
8	"fmt"
9	"os"
10	"path/filepath"
11	"strings"
12	"testing"
13
14	"github.com/stretchr/testify/assert"
15	"github.com/stretchr/testify/mock"
16)
17
18type mockDefaulter struct {
19	mock.Mock
20}
21
22func (m *mockDefaulter) defaultDataHome() string {
23	args := m.Called()
24	return args.String(0)
25}
26func (m *mockDefaulter) defaultDataDirs() []string {
27	args := m.Called()
28	return args.Get(0).([]string)
29}
30func (m *mockDefaulter) defaultConfigHome() string {
31	args := m.Called()
32	return args.String(0)
33}
34func (m *mockDefaulter) defaultConfigDirs() []string {
35	args := m.Called()
36	return args.Get(0).([]string)
37}
38func (m *mockDefaulter) defaultCacheHome() string {
39	args := m.Called()
40	return args.String(0)
41}
42
43const (
44	MDataHome = iota
45	MDataDirs
46	MConfigHome
47	MConfigDirs
48	MCacheHome
49)
50
51var getterTestCases = []getterTestCase{
52	{"DataHome Without", "defaultDataHome", filepath.Clean("/some/path"), true, "XDG_DATA_HOME", "", MDataHome, nil, filepath.Clean("/some/path")},
53	{"DataDirs Without", "defaultDataDirs", []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, true, "XDG_DATA_DIRS", "", MDataDirs, nil, []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}},
54	{"ConfigHome Without", "defaultConfigHome", filepath.Clean("/some/path"), true, "XDG_CONFIG_HOME", "", MConfigHome, nil, filepath.Clean("/some/path")},
55	{"ConfigDirs Without", "defaultConfigDirs", []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, true, "XDG_CONFIG_DIRS", "", MConfigDirs, nil, []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}},
56	{"CacheHome Without", "defaultCacheHome", filepath.Clean("/some/path"), true, "XDG_CACHE_HOME", "", MCacheHome, nil, filepath.Clean("/some/path")},
57
58	{"DataHome With", "defaultDataHome", filepath.Clean("/wrong/path"), false, "XDG_DATA_HOME", filepath.Clean("/some/path"), MDataHome, nil, filepath.Clean("/some/path")},
59	{"DataDirs With", "defaultDataDirs", []string{filepath.Clean("/wrong/path"), filepath.Clean("/some/other/wrong")}, false, "XDG_DATA_DIRS", strings.Join([]string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, string(os.PathListSeparator)), MDataDirs, nil, []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}},
60	{"ConfigHome With", "defaultConfigHome", filepath.Clean("/wrong/path"), false, "XDG_CONFIG_HOME", filepath.Clean("/some/path"), MConfigHome, nil, filepath.Clean("/some/path")},
61	{"ConfigDirs With", "defaultConfigDirs", []string{filepath.Clean("/wrong/path"), filepath.Clean("/some/other/wrong")}, false, "XDG_CONFIG_DIRS", strings.Join([]string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, string(os.PathListSeparator)), MConfigDirs, nil, []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}},
62	{"CacheHome With", "defaultCacheHome", filepath.Clean("/wrong/path"), false, "XDG_CACHE_HOME", filepath.Clean("/some/path"), MCacheHome, nil, filepath.Clean("/some/path")},
63
64	{"DataHome App Without", "defaultDataHome", filepath.Clean("/some/path"), true, "XDG_DATA_HOME", "", MDataHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")},
65	{"DataDirs App Without", "defaultDataDirs", []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, true, "XDG_DATA_DIRS", "", MDataDirs, New("OpenPeeDeeP", "XDG"), []string{filepath.Clean("/some/path/OpenPeeDeeP/XDG"), filepath.Clean("/some/other/path/OpenPeeDeeP/XDG")}},
66	{"ConfigHome App Without", "defaultConfigHome", filepath.Clean("/some/path"), true, "XDG_CONFIG_HOME", "", MConfigHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")},
67	{"ConfigDirs App Without", "defaultConfigDirs", []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, true, "XDG_CONFIG_DIRS", "", MConfigDirs, New("OpenPeeDeeP", "XDG"), []string{filepath.Clean("/some/path/OpenPeeDeeP/XDG"), filepath.Clean("/some/other/path/OpenPeeDeeP/XDG")}},
68	{"CacheHome App Without", "defaultCacheHome", filepath.Clean("/some/path"), true, "XDG_CACHE_HOME", "", MCacheHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")},
69
70	{"DataHome App With", "defaultDataHome", filepath.Clean("/wrong/path"), false, "XDG_DATA_HOME", filepath.Clean("/some/path"), MDataHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")},
71	{"DataDirs App With", "defaultDataDirs", []string{filepath.Clean("/wrong/path"), filepath.Clean("/some/other/wrong")}, false, "XDG_DATA_DIRS", strings.Join([]string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, string(os.PathListSeparator)), MDataDirs, New("OpenPeeDeeP", "XDG"), []string{filepath.Clean("/some/path/OpenPeeDeeP/XDG"), filepath.Clean("/some/other/path/OpenPeeDeeP/XDG")}},
72	{"ConfigHome App With", "defaultConfigHome", filepath.Clean("/wrong/path"), false, "XDG_CONFIG_HOME", filepath.Clean("/some/path"), MConfigHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")},
73	{"ConfigDirs App With", "defaultConfigDirs", []string{filepath.Clean("/wrong/path"), filepath.Clean("/some/other/wrong")}, false, "XDG_CONFIG_DIRS", strings.Join([]string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, string(os.PathListSeparator)), MConfigDirs, New("OpenPeeDeeP", "XDG"), []string{filepath.Clean("/some/path/OpenPeeDeeP/XDG"), filepath.Clean("/some/other/path/OpenPeeDeeP/XDG")}},
74	{"CacheHome App With", "defaultCacheHome", filepath.Clean("/wrong/path"), false, "XDG_CACHE_HOME", filepath.Clean("/some/path"), MCacheHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")},
75}
76
77type getterTestCase struct {
78	name         string
79	mokedMethod  string
80	mockedReturn interface{}
81	calledMocked bool
82	env          string
83	envVal       string
84	method       int
85	xdgApp       *XDG
86	expected     interface{}
87}
88
89func TestXDG_Getters(t *testing.T) {
90	for _, tc := range getterTestCases {
91		t.Run(tc.name, func(t *testing.T) {
92			assert := assert.New(t)
93			mockDef := new(mockDefaulter)
94			mockDef.On(tc.mokedMethod).Return(tc.mockedReturn)
95			setDefaulter(mockDef)
96			os.Setenv(tc.env, tc.envVal) // nolint: errcheck
97
98			actual := computeActual(tc)
99
100			if tc.calledMocked {
101				mockDef.AssertExpectations(t)
102			} else {
103				mockDef.AssertNotCalled(t, tc.mokedMethod)
104			}
105			assert.Equal(tc.expected, actual)
106		})
107	}
108}
109
110// nolint: gocyclo
111func computeActual(tc getterTestCase) interface{} {
112	var actual interface{}
113	switch tc.method {
114	case MDataHome:
115		if tc.xdgApp != nil {
116			actual = tc.xdgApp.DataHome()
117		} else {
118			actual = DataHome()
119		}
120	case MDataDirs:
121		if tc.xdgApp != nil {
122			actual = tc.xdgApp.DataDirs()
123		} else {
124			actual = DataDirs()
125		}
126	case MConfigHome:
127		if tc.xdgApp != nil {
128			actual = tc.xdgApp.ConfigHome()
129		} else {
130			actual = ConfigHome()
131		}
132	case MConfigDirs:
133		if tc.xdgApp != nil {
134			actual = tc.xdgApp.ConfigDirs()
135		} else {
136			actual = ConfigDirs()
137		}
138	case MCacheHome:
139		if tc.xdgApp != nil {
140			actual = tc.xdgApp.CacheHome()
141		} else {
142			actual = CacheHome()
143		}
144	}
145	return actual
146}
147
148const (
149	QData = iota
150	QConfig
151	QCache
152)
153
154var (
155	root      = "testingFolder"
156	fileTypes = []string{"data", "config", "cache"}
157	fileLoc   = []string{"home", "dirs"}
158)
159
160type queryTestCase struct {
161	name      string
162	xdgApp    *XDG
163	queryType int
164	filename  string
165	expected  string
166}
167
168var queryTestCases = []queryTestCase{
169	{"Data Dirs", New("OpenPeeDeeP", "XDG"), QData, "XDG_DATA_DIRS.txt", filepath.Clean("/data/dirs/OpenPeeDeeP/XDG/XDG_DATA_DIRS.txt")},
170	{"Data Home", New("OpenPeeDeeP", "XDG"), QData, "XDG_DATA_HOME.txt", filepath.Clean("/data/home/OpenPeeDeeP/XDG/XDG_DATA_HOME.txt")},
171	{"Data DNE", New("OpenPeeDeeP", "XDG"), QData, "XDG_CONFIG_HOME.txt", ""},
172
173	{"Config Dirs", New("OpenPeeDeeP", "XDG"), QConfig, "XDG_CONFIG_DIRS.txt", filepath.Clean("/config/dirs/OpenPeeDeeP/XDG/XDG_CONFIG_DIRS.txt")},
174	{"Config Home", New("OpenPeeDeeP", "XDG"), QConfig, "XDG_CONFIG_HOME.txt", filepath.Clean("/config/home/OpenPeeDeeP/XDG/XDG_CONFIG_HOME.txt")},
175	{"Config DNE", New("OpenPeeDeeP", "XDG"), QConfig, "XDG_DATA_HOME.txt", ""},
176
177	{"Cache Home", New("OpenPeeDeeP", "XDG"), QCache, "XDG_CACHE_HOME.txt", filepath.Clean("/cache/home/OpenPeeDeeP/XDG/XDG_CACHE_HOME.txt")},
178	{"Cache DNE", New("OpenPeeDeeP", "XDG"), QCache, "XDG_CACHE_DIRS.txt", ""},
179}
180
181func TestXDG_Query(t *testing.T) {
182	for _, tc := range queryTestCases {
183		t.Run(tc.name, func(t *testing.T) {
184			defer teardownQueryData() //nolint: errcheck
185			standupQueryData(tc)      //nolint: errcheck
186			assert := assert.New(t)
187			actual := computeQuery(tc)
188			assert.Equal(tc.expected, actual)
189		})
190	}
191}
192
193func computeQuery(tc queryTestCase) string {
194	var actual string
195	switch tc.queryType {
196	case QData:
197		actual = tc.xdgApp.QueryData(tc.filename)
198	case QCache:
199		actual = tc.xdgApp.QueryCache(tc.filename)
200	case QConfig:
201		actual = tc.xdgApp.QueryConfig(tc.filename)
202	}
203	rootAbs, _ := filepath.Abs(root)
204	actual = strings.Replace(actual, rootAbs, "", 1)
205	return actual
206}
207
208func standupQueryData(tc queryTestCase) error {
209	for _, t := range fileTypes {
210		for _, l := range fileLoc {
211			path, err := filepath.Abs(filepath.Join(root, t, l))
212			if err != nil {
213				return err
214			}
215			if err = os.MkdirAll(filepath.Join(path, tc.xdgApp.Vendor, tc.xdgApp.Application), 0777); err != nil {
216				return err
217			}
218			envVar := fmt.Sprintf("XDG_%s_%s", strings.ToUpper(t), strings.ToUpper(l))
219			if err = os.Setenv(envVar, path); err != nil {
220				return err
221			}
222			file, err := os.OpenFile(filepath.Join(path, tc.xdgApp.Vendor, tc.xdgApp.Application, envVar+".txt"), os.O_CREATE|os.O_RDONLY, 0666)
223			if err != nil {
224				return err
225			}
226			defer file.Close() //nolint: errcheck
227		}
228	}
229	return nil
230}
231
232func teardownQueryData() error {
233	return os.RemoveAll(root)
234}
235