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  }