github.com/atrn/dcc@v0.0.0-20220806184050-4470d2553272/options_test.go (about) 1 // dcc - dependency-driven C/C++ compiler front end 2 // 3 // Copyright © A.Newman 2015. 4 // 5 // This source code is released under version 2 of the GNU Public License. 6 // See the file LICENSE for details. 7 // 8 9 package main 10 11 import ( 12 "fmt" 13 "os" 14 "path/filepath" 15 "runtime" 16 "strings" 17 "testing" 18 ) 19 20 func expectValues(t *testing.T, options *Options, expectedValues []string) { 21 if len(options.Values) != len(expectedValues) { 22 t.Fatalf("read %d option values but expected %d\nactual: %#v\nexpected: %#v", len(options.Values), len(expectedValues), options.Values, expectedValues) 23 } else { 24 for index, value := range expectedValues { 25 if value != expectedValues[index] { 26 t.Fatalf("option value %d - %q but expected %q", index, value, expectedValues[index]) 27 } 28 } 29 } 30 } 31 32 func readOptionsFromString(data string) (*Options, error) { 33 options := new(Options) 34 r := strings.NewReader(data) 35 _, err := options.ReadFromReader(r, "<data>", nil) 36 return options, err 37 } 38 39 func readOptionsFromFile(t *testing.T, path string) (*Options, error) { 40 options := NewOptions() 41 ok, err := options.ReadFromFile(path, nil) 42 if err != nil { 43 return nil, err 44 } 45 if !ok { 46 return nil, fmt.Errorf("failed to read options from file %q", path) 47 } 48 return options, err 49 } 50 51 func mustReadOptionsFromFile(t *testing.T, path string) *Options { 52 options, err := readOptionsFromFile(t, path) 53 if err != nil { 54 t.Fatal(err) 55 } 56 return options 57 } 58 59 func mustReadOptionsFromString(t *testing.T, data string) *Options { 60 options, err := readOptionsFromString(data) 61 if err != nil { 62 t.Fatal(err) 63 } 64 return options 65 } 66 67 func testOptions(t *testing.T, data string, expectedValues []string) { 68 options := mustReadOptionsFromString(t, data) 69 expectValues(t, options, expectedValues) 70 } 71 72 func TestComments(t *testing.T) { 73 data := ` 74 # This is a comment 75 value1 76 value2 77 # This is not a comment 78 # This is another comment 79 ` 80 testOptions(t, data, []string{"value1", "value2", "#", "This", "is", "not", "a", "comment"}) 81 } 82 83 func TestEnvVars(t *testing.T) { 84 data := ` 85 # comment 86 $AVAR $BVAR 87 ` 88 testOptions(t, data, []string{}) 89 90 os.Setenv("AVAR", "a-var-value") 91 testOptions(t, data, []string{"a-var-value"}) 92 93 os.Setenv("AVAR", "value1 value2") 94 testOptions(t, data, []string{"value1", "value2"}) 95 96 os.Setenv("BVAR", "") 97 testOptions(t, data, []string{"value1", "value2"}) 98 99 os.Setenv("BVAR", "value3") 100 testOptions(t, data, []string{"value1", "value2", "value3"}) 101 } 102 103 func TestConditionals(t *testing.T) { 104 data := ` 105 !ifdef PATH 106 a-value 107 !endif 108 ` 109 testOptions(t, data, []string{"a-value"}) 110 111 data = ` 112 !ifdef NOTDEF 113 a-value 114 !endif 115 ` 116 testOptions(t, data, []string{}) 117 118 data = ` 119 !ifndef NOTDEF 120 a-value 121 !endif 122 ` 123 testOptions(t, data, []string{"a-value"}) 124 } 125 126 func TestElse(t *testing.T) { 127 data := ` 128 !ifdef NOTDEF 129 a-value 130 !else 131 b-value 132 !endif 133 c-value 134 ` 135 testOptions(t, data, []string{"b-value", "c-value"}) 136 } 137 138 func TestNestedIfdef(t *testing.T) { 139 data := ` 140 !ifdef PATH 141 !ifdef NOTDEF 142 a-value 143 !else 144 b-value 145 !endif 146 c-value 147 !endif 148 d-value 149 ` 150 testOptions(t, data, []string{"b-value", "c-value", "d-value"}) 151 152 data = ` 153 !ifdef PATH 154 !ifndef NOTDEF 155 a-value 156 !else 157 b-value 158 !endif 159 c-value 160 !endif 161 d-value 162 ` 163 testOptions(t, data, []string{"a-value", "c-value", "d-value"}) 164 } 165 166 func TestError(t *testing.T) { 167 errorMessage := "Something bad happened" 168 169 data := fmt.Sprintf(` 170 !error %s 171 `, 172 errorMessage, 173 ) 174 175 _, err := readOptionsFromString(data) 176 if err == nil { 177 t.Fail() 178 } 179 if !strings.HasSuffix(err.Error(), errorMessage) { 180 t.Fatalf("!error produced unexpected error %q, expected %q", err.Error(), errorMessage) 181 } 182 183 data = fmt.Sprintf(` 184 !ifdef NOTDEF 185 !error %s 186 !endif 187 `, 188 errorMessage, 189 ) 190 _, err = readOptionsFromString(data) 191 if err != nil { 192 t.Fatalf("!error within false condition triggered with message %q", err.Error()) 193 } 194 195 knownEnvVar := "HOME" 196 if runtime.GOOS == "windows" { 197 knownEnvVar = "USERPROFILE" 198 } 199 data = fmt.Sprintf(` 200 !ifdef %s 201 !error %s 202 !endif 203 `, 204 knownEnvVar, 205 errorMessage, 206 ) 207 _, err = readOptionsFromString(data) 208 if err == nil { 209 t.Fatal("!error within true condition did not return expected error") 210 } 211 if !strings.HasSuffix(err.Error(), errorMessage) { 212 t.Fatalf("!error within true condition produced unexpected error %q, expected %q", err.Error(), errorMessage) 213 } 214 } 215 216 func TestInclude(t *testing.T) { 217 setupTest(t) 218 defer removeTestDirs(t) 219 includeFilename := "included.options" 220 includedValue := "included-value" 221 222 includedFile := fmt.Sprintf( 223 `# This file is included 224 %s 225 `, 226 includedValue, 227 ) 228 229 includingFile := fmt.Sprintf(`# This file includes another 230 !include %q 231 `, 232 includeFilename, 233 ) 234 235 filename := filepath.Join(testProjectChildDir, testFilename) 236 makeFileWithContent(t, filename, includingFile) 237 makeFileWithContent(t, filepath.Join(testProjectChildDir, includeFilename), includedFile) 238 options := mustReadOptionsFromFile(t, filename) 239 expectValues(t, options, []string{includedValue}) 240 } 241 242 func TestIncludeNonExistentFile(t *testing.T) { 243 setupTest(t) 244 defer removeTestDirs(t) 245 includeFilename := "included.options" 246 247 includingFile := fmt.Sprintf(`# This file includes another 248 !include %q 249 `, 250 includeFilename, 251 ) 252 253 filename := filepath.Join(testProjectChildDir, testFilename) 254 makeFileWithContent(t, filename, includingFile) 255 _, err := readOptionsFromFile(t, filename) 256 if err == nil { 257 t.Fatalf("!include of non-existent file did not fail") 258 } 259 } 260 261 func TestInherit(t *testing.T) { 262 setupTest(t) 263 defer removeTestDirs(t) 264 265 directValue := "direct-value" 266 inheritedValue := "inherited-value" 267 268 fileInChildDir := filepath.Join(testProjectChildDir, testFilename) 269 fileInParentDir := filepath.Join(testProjectRootDir, testFilename) 270 271 makeFileWithContent(t, fileInParentDir, inheritedValue) 272 makeFileWithContent(t, fileInChildDir, fmt.Sprintf("%s\n!inherit\n", directValue)) 273 274 options := mustReadOptionsFromFile(t, fileInChildDir) 275 expectValues(t, options, []string{directValue, inheritedValue}) 276 } 277 278 func TestInheritFromPlatformSpecificFile(t *testing.T) { 279 setupTest(t) 280 defer removeTestDirs(t) 281 282 directValue := "direct-value" 283 inheritedValue := "inherited-value" 284 285 fileInChildDir := filepath.Join(testProjectChildDir, OsSpecificFilename(testFilename)) 286 fileInParentDir := filepath.Join(testProjectRootDir, testFilename) 287 288 makeFileWithContent(t, fileInParentDir, inheritedValue) 289 makeFileWithContent(t, fileInChildDir, fmt.Sprintf("%s\n!inherit\n", directValue)) 290 291 options := mustReadOptionsFromFile(t, fileInChildDir) 292 expectValues(t, options, []string{directValue, inheritedValue}) 293 } 294 295 func TestInheritPlatformSpecificFileFromPlainFile(t *testing.T) { 296 setupTest(t) 297 defer removeTestDirs(t) 298 299 directValue := "direct-value" 300 inheritedValue := "inherited-value" 301 302 fileInChildDir := filepath.Join(testProjectChildDir, testFilename) 303 fileInParentDir := filepath.Join(testProjectRootDir, OsSpecificFilename(testFilename)) 304 305 makeFileWithContent(t, fileInParentDir, inheritedValue) 306 makeFileWithContent(t, fileInChildDir, fmt.Sprintf("%s\n!inherit\n", directValue)) 307 308 options := mustReadOptionsFromFile(t, fileInChildDir) 309 expectValues(t, options, []string{directValue, inheritedValue}) 310 } 311 312 func TestInheritFile(t *testing.T) { 313 setupTest(t) 314 defer removeTestDirs(t) 315 316 directValue := "direct-value" 317 inheritedValue := "inherited-value" 318 319 inheritedFilename := "inherited.file" 320 321 fileInChildDir := filepath.Join(testProjectChildDir, testFilename) 322 fileInParentDir := filepath.Join(testProjectRootDir, inheritedFilename) 323 324 makeFileWithContent(t, fileInParentDir, inheritedValue) 325 makeFileWithContent(t, fileInChildDir, fmt.Sprintf("%s\n!inherit %s\n", directValue, filepath.Base(fileInParentDir))) 326 327 options := mustReadOptionsFromFile(t, fileInChildDir) 328 expectValues(t, options, []string{directValue, inheritedValue}) 329 } 330 331 func TestInheritPlatformSpecificFile(t *testing.T) { 332 setupTest(t) 333 defer removeTestDirs(t) 334 335 directValue := "direct-value" 336 inheritedValue := "inherited-value" 337 338 inheritedFilename := "inherited.file" 339 340 fileInChildDir := filepath.Join(testProjectChildDir, testFilename) 341 fileInParentDir := filepath.Join(testProjectRootDir, OsSpecificFilename(inheritedFilename)) 342 343 makeFileWithContent(t, fileInParentDir, inheritedValue) 344 makeFileWithContent(t, fileInChildDir, fmt.Sprintf("%s\n!inherit %s\n", directValue, filepath.Base(fileInParentDir))) 345 346 options := mustReadOptionsFromFile(t, fileInChildDir) 347 expectValues(t, options, []string{directValue, inheritedValue}) 348 }