diff --git a/flecmd/adif.go b/flecmd/adif.go index f295868..3c941bf 100644 --- a/flecmd/adif.go +++ b/flecmd/adif.go @@ -52,6 +52,14 @@ var adifCmd = &cobra.Command{ return fmt.Errorf("Too many arguments.%s", "") } + // Verify given output directory exists. This check should be performed + // Before running any long process so as to not make the user wait and + // then be notified the file cannot be written. + dirErr := CheckDir(outputFilename) + if dirErr != nil { + return dirErr + } + var adifParam = new(fleprocess.AdifParams) adifParam.InputFilename = inputFilename adifParam.OutputFilename = outputFilename diff --git a/flecmd/adif_test.go b/flecmd/adif_test.go new file mode 100644 index 0000000..3e18354 --- /dev/null +++ b/flecmd/adif_test.go @@ -0,0 +1,75 @@ +/* +Copyright © 2024 Jean-Marc Meessen jean-marc@meessen-web.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package flecmd + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_AdifWithoutParmMustFail(t *testing.T) { + actual := new(bytes.Buffer) + rootCmd.SetOut(actual) + rootCmd.SetErr(actual) + rootCmd.SetArgs([]string{"adif"}) + error := rootCmd.Execute() + + assert.Error(t, error, "Function call should have failed") + + // Error is expected + expectedMsg := "Error: missing input file " + lines := strings.Split(actual.String(), "\n") + assert.Equal(t, expectedMsg, lines[0], "Function did not fail for the expected cause") +} + +func Test_AdifWithToManyParmMustFail(t *testing.T) { + actual := new(bytes.Buffer) + rootCmd.SetOut(actual) + rootCmd.SetErr(actual) + rootCmd.SetArgs([]string{"adif", "param1", "param2", "param3"}) + error := rootCmd.Execute() + + assert.Error(t, error, "Function call should have failed") + + // Error is expected + expectedMsg := "Error: Too many arguments." + lines := strings.Split(actual.String(), "\n") + assert.Equal(t, expectedMsg, lines[0], "Function did not fail for the expected cause") +} + +func Test_AdifBadOutpoutDirMustFail(t *testing.T) { + actual := new(bytes.Buffer) + rootCmd.SetOut(actual) + rootCmd.SetErr(actual) + rootCmd.SetArgs([]string{"adif", "../test/data/fle-1.txt", "badDirectory/outputfile.adi"}) + error := rootCmd.Execute() + + assert.Error(t, error, "Function call should have failed") + + // Error is expected + expectedMsg := "Error: The directory of specified output file (badDirectory) does not exist." + lines := strings.Split(actual.String(), "\n") + assert.Equal(t, expectedMsg, lines[0], "Function did not fail for the expected cause") +} diff --git a/flecmd/csv.go b/flecmd/csv.go index da12644..01b2e5b 100644 --- a/flecmd/csv.go +++ b/flecmd/csv.go @@ -51,6 +51,14 @@ func csvCmdConstructor() *cobra.Command { return fmt.Errorf("Too many arguments.%s", "") } + // Verify given output directory exists. This check should be performed + // Before running any long process so as to not make the user wait and + // then be notified the file cannot be written. + dirErr := CheckDir(outputCsvFilename) + if dirErr != nil { + return dirErr + } + if err := fleprocess.ProcessCsvCommand(inputFilename, outputCsvFilename, isInterpolateTime, isOverwriteCsv); err != nil { fmt.Println("\nUnable to generate CSV file:") fmt.Println(err) diff --git a/flecmd/csv_test.go b/flecmd/csv_test.go new file mode 100644 index 0000000..3c75582 --- /dev/null +++ b/flecmd/csv_test.go @@ -0,0 +1,75 @@ +/* +Copyright © 2024 Jean-Marc Meessen jean-marc@meessen-web.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package flecmd + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_CsvWithoutParmMustFail(t *testing.T) { + actual := new(bytes.Buffer) + rootCmd.SetOut(actual) + rootCmd.SetErr(actual) + rootCmd.SetArgs([]string{"csv"}) + error := rootCmd.Execute() + + assert.Error(t, error, "Function call should have failed") + + // Error is expected + expectedMsg := "Error: Missing input file " + lines := strings.Split(actual.String(), "\n") + assert.Equal(t, expectedMsg, lines[0], "Function did not fail for the expected cause") +} + +func Test_CsvWithToManyParmMustFail(t *testing.T) { + actual := new(bytes.Buffer) + rootCmd.SetOut(actual) + rootCmd.SetErr(actual) + rootCmd.SetArgs([]string{"csv", "param1", "param2", "param3"}) + error := rootCmd.Execute() + + assert.Error(t, error, "Function call should have failed") + + // Error is expected + expectedMsg := "Error: Too many arguments." + lines := strings.Split(actual.String(), "\n") + assert.Equal(t, expectedMsg, lines[0], "Function did not fail for the expected cause") +} + +func Test_CsvBadOutpoutDirMustFail(t *testing.T) { + actual := new(bytes.Buffer) + rootCmd.SetOut(actual) + rootCmd.SetErr(actual) + rootCmd.SetArgs([]string{"csv", "../test/data/fle-1.txt", "badDirectory/outputfile.adi"}) + error := rootCmd.Execute() + + assert.Error(t, error, "Function call should have failed") + + // Error is expected + expectedMsg := "Error: The directory of specified output file (badDirectory) does not exist." + lines := strings.Split(actual.String(), "\n") + assert.Equal(t, expectedMsg, lines[0], "Function did not fail for the expected cause") +} diff --git a/flecmd/root.go b/flecmd/root.go index d903bf6..7ffa8d8 100644 --- a/flecmd/root.go +++ b/flecmd/root.go @@ -25,6 +25,7 @@ THE SOFTWARE. import ( "fmt" "os" + "path/filepath" "github.com/spf13/cobra" @@ -83,3 +84,15 @@ func initConfig() { fmt.Println("Using config file:", viper.ConfigFileUsed()) } } + +// CheckDir verifies a given path/file string actually exists. If it does not +// then exit with an error. +func CheckDir(file string) error { + path := filepath.Dir(file) + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("The directory of specified output file (%s) does not exist.", path) + } + } + return nil +} diff --git a/flecmd/root_test.go b/flecmd/root_test.go new file mode 100644 index 0000000..4ad7bef --- /dev/null +++ b/flecmd/root_test.go @@ -0,0 +1,53 @@ +/* +Copyright © 2024 Jean-Marc Meessen jean-marc@meessen-web.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package flecmd + +import "testing" + +func TestCheckDir(t *testing.T) { + type args struct { + file string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + "Valid directory", + args{file: "../test/data/fle-1.txt"}, + false, + }, + { + "Invalid directory", + args{file: "../junkDir/fle-1.txt"}, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := CheckDir(tt.args.file); (err != nil) != tt.wantErr { + t.Errorf("CheckDir() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/fleprocess/adif_write.go b/fleprocess/adif_write.go index a7e3d20..1274759 100644 --- a/fleprocess/adif_write.go +++ b/fleprocess/adif_write.go @@ -93,6 +93,13 @@ func buildAdif(fullLog []LogLine, adifParams AdifParams) (adifList []string) { if logLine.MyGrid != "" { adifLine.WriteString(adifElement("MY_GRIDSQUARE", logLine.MyGrid)) } + + if logLine.MyLat != "" { + adifLine.WriteString(adifElement("MY_LAT", logLine.MyLat)) + } + if logLine.MyLon != "" { + adifLine.WriteString(adifElement("MY_LON", logLine.MyLon)) + } if logLine.MyCounty != "" { adifLine.WriteString(adifElement("MY_CNTY", logLine.MyCounty)) } diff --git a/fleprocess/adif_write_test.go b/fleprocess/adif_write_test.go index 74b84c8..782cc47 100644 --- a/fleprocess/adif_write_test.go +++ b/fleprocess/adif_write_test.go @@ -111,6 +111,7 @@ func Test_buildAdif(t *testing.T) { sampleFilledLogPOTA2 := []LogLine{ {MyCall: "ON4KJM/P", Call: "S57LC", Date: "2020-05-24", MyGrid: "JO40eu", Time: "1310", Band: "20m", Frequency: "14.045", Mode: "CW", RSTsent: "599", RSTrcvd: "599", GridLoc: "JO50", MyPOTA: "ON-00259", Operator: "ON4KJM", Nickname: "ON-00259-1"}, {MyCall: "ON4KJM/P", Call: "ON4LY", Date: "2020-05-24", MyGrid: "JO40eu", Time: "1312", Band: "20m", Mode: "CW", RSTsent: "559", RSTrcvd: "599", MyPOTA: "ON-00259", Operator: "ON4KJM", POTA: "DL-00001"}, + {MyCall: "ON4KJM/P", Call: "ON4LY", Date: "2020-05-24", MyGrid: "JO40eu", Time: "1312", Band: "20m", Mode: "CW", RSTsent: "559", RSTrcvd: "599", MyPOTA: "ON-00259", Operator: "ON4KJM", POTA: "DL-00001", MyLat: "15.1234567", MyLon: "-123.1234567"}, } expectedOutputPOTA2 := []string{ @@ -120,6 +121,7 @@ func Test_buildAdif(t *testing.T) { "", "ON4KJM/P S57LC 20200524 1310 20m CW 14.045 599 599 JO50 POTA ON-00259 ON4KJM JO40eu ON-00259-1 ", "ON4KJM/P ON4LY 20200524 1312 20m CW 559 599 POTA ON-00259 POTA DL-00001 ON4KJM JO40eu ", + "ON4KJM/P ON4LY 20200524 1312 20m CW 559 599 POTA ON-00259 POTA DL-00001 ON4KJM JO40eu 15.1234567 -123.1234567 ", } type args struct { diff --git a/fleprocess/displayLog.go b/fleprocess/displayLog.go index a96252c..99d19c2 100644 --- a/fleprocess/displayLog.go +++ b/fleprocess/displayLog.go @@ -79,6 +79,14 @@ func SprintHeaderValues(logLine LogLine) string { output.WriteString("MyGrid " + logLine.MyGrid + "\n") } + if logLine.MyLat != "" { + output.WriteString("MyLat " + logLine.MyLat + "\n") + } + + if logLine.MyLon != "" { + output.WriteString("MyLon " + logLine.MyLon + "\n") + } + if logLine.MyCounty != "" { output.WriteString("MyCounty " + logLine.MyCounty + "\n") } diff --git a/fleprocess/load_file.go b/fleprocess/load_file.go index d764078..d174d1e 100644 --- a/fleprocess/load_file.go +++ b/fleprocess/load_file.go @@ -64,6 +64,8 @@ func LoadFile(inputFilename string, isInterpolateTime bool) (filleFullLog []LogL regexpHeaderMySota := regexp.MustCompile(`(?i)^mysota\s+`) regexpHeaderMyPota := regexp.MustCompile(`(?i)^mypota\s+`) regexpHeaderMyGrid := regexp.MustCompile(`(?i)^mygrid\s+`) + regexpHeaderMyLat := regexp.MustCompile(`(?i)^mylat\s+`) + regexpHeaderMyLon := regexp.MustCompile(`(?i)^mylon\s+`) regexpHeaderMyCounty := regexp.MustCompile(`(?i)^mycounty\s+`) regexpHeaderQslMsg := regexp.MustCompile(`(?i)^qslmsg\s+`) regexpHeaderNickname := regexp.MustCompile(`(?i)^nickname\s+`) @@ -74,6 +76,8 @@ func LoadFile(inputFilename string, isInterpolateTime bool) (filleFullLog []LogL headerMySOTA := "" headerMyPOTA := "" headerMyGrid := "" + headerMyLat := "" + headerMyLon := "" headerMyCounty := "" headerQslMsg := "" headerNickname := "" @@ -249,6 +253,44 @@ func LoadFile(inputFilename string, isInterpolateTime bool) (filleFullLog []LogL continue } + //My Lat + if regexpHeaderMyLat.MatchString(eachline) { + //Attempt to redefine value + if headerMyLat != "" { + errorLog = append(errorLog, fmt.Sprintf("Attempt to redefine MyLat at line %d", lineCount)) + continue + } + errorMsg := "" + myLatList := regexpHeaderMyLat.Split(eachline, -1) + if len(strings.TrimSpace(myLatList[1])) > 0 { + headerMyLat, errorMsg = ValidateLat(strings.TrimSpace(myLatList[1])) + if len(errorMsg) != 0 { + errorLog = append(errorLog, fmt.Sprintf("Invalid \"My Lat\" at line %d: %s (%s)", lineCount, myLatList[1], errorMsg)) + } + } + //If there is no data after the marker, we just skip the data. + continue + } + + //My Lon + if regexpHeaderMyLon.MatchString(eachline) { + //Attempt to redefine value + if headerMyLon != "" { + errorLog = append(errorLog, fmt.Sprintf("Attempt to redefine MyLon at line %d", lineCount)) + continue + } + errorMsg := "" + myLonList := regexpHeaderMyLon.Split(eachline, -1) + if len(strings.TrimSpace(myLonList[1])) > 0 { + headerMyLon, errorMsg = ValidateLon(strings.TrimSpace(myLonList[1])) + if len(errorMsg) != 0 { + errorLog = append(errorLog, fmt.Sprintf("Invalid \"My Lon\" at line %d: %s (%s)", lineCount, myLonList[1], errorMsg)) + } + } + //If there is no data after the marker, we just skip the data. + continue + } + //My County if regexpHeaderMyCounty.MatchString(eachline) { myMyCountyList := regexpHeaderMyCounty.Split(eachline, -1) @@ -298,6 +340,8 @@ func LoadFile(inputFilename string, isInterpolateTime bool) (filleFullLog []LogL previousLogLine.MyPOTA = headerMyPOTA previousLogLine.MySOTA = headerMySOTA previousLogLine.MyGrid = headerMyGrid + previousLogLine.MyLat = headerMyLat + previousLogLine.MyLon = headerMyLon previousLogLine.MyCounty = headerMyCounty previousLogLine.QSLmsg = headerQslMsg //previousLogLine.QslMsg is redundant previousLogLine.Nickname = headerNickname diff --git a/fleprocess/load_file_test.go b/fleprocess/load_file_test.go index 401366c..d40cc9e 100644 --- a/fleprocess/load_file_test.go +++ b/fleprocess/load_file_test.go @@ -44,6 +44,8 @@ func TestLoadFile_happyCase(t *testing.T) { dataArray = append(dataArray, "mySota on/on-001") dataArray = append(dataArray, "myPota k-0802") dataArray = append(dataArray, "myGrid jo50") + dataArray = append(dataArray, "myLat 55.5555555") + dataArray = append(dataArray, "myLon -120.01234567") dataArray = append(dataArray, "myCounty Ham County") dataArray = append(dataArray, "QslMsg This is a QSL message") dataArray = append(dataArray, " ") @@ -86,6 +88,14 @@ func TestLoadFile_happyCase(t *testing.T) { if loadedLogFile[0].MySOTA != expectedValue { t.Errorf("Not the expected MySOTA value: %s (expecting %s)", loadedLogFile[0].MySOTA, expectedValue) } + expectedValue = "55.5555555" + if loadedLogFile[0].MyLat != expectedValue { + t.Errorf("Not the expected MyLat value: %s (expecting %s)", loadedLogFile[0].MyLat, expectedValue) + } + expectedValue = "-120.01234567" + if loadedLogFile[0].MyLon != expectedValue { + t.Errorf("Not the expected MyLon value: %s (expecting %s)", loadedLogFile[0].MyLon, expectedValue) + } expectedValue = "This is a QSL message" if loadedLogFile[0].QSLmsg != expectedValue { t.Errorf("Not the expected QSL Message from Header value: %s (expecting %s)", loadedLogFile[0].QSLmsg, expectedValue) @@ -388,6 +398,166 @@ func TestLoadFile_happyCase_day(t *testing.T) { os.Remove(temporaryDataFileName) } +func TestLoadFile_redefining_myLat_must_fail(t *testing.T) { + + //Given + dataArray := make([]string, 0) + dataArray = append(dataArray, "# Header") + dataArray = append(dataArray, "myCall on4kjm/p") + dataArray = append(dataArray, "operator on4kjm") + dataArray = append(dataArray, "nickname Portable") + dataArray = append(dataArray, "myLat -55.55") + dataArray = append(dataArray, " ") + dataArray = append(dataArray, " #Log") + dataArray = append(dataArray, "20/5/23") + dataArray = append(dataArray, "40m cw 0950 ik5zve/5 9 5") + dataArray = append(dataArray, "myLat 55.55") + + temporaryDataFileName := createTestFile(dataArray) + + //When + loadedLogFile, isLoadedOK := LoadFile(temporaryDataFileName, true) + + //Then + if isLoadedOK { + t.Error("Test file processing should return with an error") + } + if len(loadedLogFile) == 0 { + t.Error("No data loaded") + } + + expectedValue := "-55.55" + if loadedLogFile[0].MyLat != expectedValue { + t.Errorf("Not the expected MyLat value: %s (expecting %s)", loadedLogFile[0].MyLat, expectedValue) + } + + //Clean Up + os.Remove(temporaryDataFileName) +} + +func TestLoadFile_redefining_myLon_must_fail(t *testing.T) { + + //Given + dataArray := make([]string, 0) + dataArray = append(dataArray, "# Header") + dataArray = append(dataArray, "myCall on4kjm/p") + dataArray = append(dataArray, "operator on4kjm") + dataArray = append(dataArray, "nickname Portable") + dataArray = append(dataArray, "myLon -55.55") + dataArray = append(dataArray, " ") + dataArray = append(dataArray, " #Log") + dataArray = append(dataArray, "20/5/23") + dataArray = append(dataArray, "40m cw 0950 ik5zve/5 9 5") + dataArray = append(dataArray, "myLon 55.55") + + temporaryDataFileName := createTestFile(dataArray) + + //When + loadedLogFile, isLoadedOK := LoadFile(temporaryDataFileName, true) + + //Then + if isLoadedOK { + t.Error("Test file processing should return with an error") + } + if len(loadedLogFile) == 0 { + t.Error("No data loaded") + } + + expectedValue := "-55.55" + if loadedLogFile[0].MyLon != expectedValue { + t.Errorf("Not the expected MyLon value: %s (expecting %s)", loadedLogFile[0].MyLon, expectedValue) + } + + //Clean Up + os.Remove(temporaryDataFileName) +} + +func Outofrange_myLonHelper(t *testing.T, val string) { + //Given + dataArray := make([]string, 0) + dataArray = append(dataArray, "# Header") + dataArray = append(dataArray, "myCall on4kjm/p") + dataArray = append(dataArray, "operator on4kjm") + dataArray = append(dataArray, "nickname Portable") + dataArray = append(dataArray, "myLon "+val) + dataArray = append(dataArray, " ") + dataArray = append(dataArray, " #Log") + dataArray = append(dataArray, "20/5/23") + dataArray = append(dataArray, "40m cw 0950 ik5zve/5 9 5") + + temporaryDataFileName := createTestFile(dataArray) + + //When + loadedLogFile, isLoadedOK := LoadFile(temporaryDataFileName, true) + + //Then + if isLoadedOK { + t.Error("Test file processing should return with an error") + } + if len(loadedLogFile) == 0 { + t.Error("No data loaded") + } + + expectedValue := "*" + val + if loadedLogFile[0].MyLon != expectedValue { + t.Errorf("Not the expected MyLon value: %s (expecting %s)", loadedLogFile[0].MyLon, expectedValue) + } + + //Clean Up + os.Remove(temporaryDataFileName) +} + +func TestLoadFile_outofrange_high_myLon_must_fail(t *testing.T) { + Outofrange_myLonHelper(t, "180.0001") +} + +func TestLoadFile_outofrange_low_myLon_must_fail(t *testing.T) { + Outofrange_myLonHelper(t, "-180.0001") +} + +func Outofrange_myLatHelper(t *testing.T, val string) { + //Given + dataArray := make([]string, 0) + dataArray = append(dataArray, "# Header") + dataArray = append(dataArray, "myCall on4kjm/p") + dataArray = append(dataArray, "operator on4kjm") + dataArray = append(dataArray, "nickname Portable") + dataArray = append(dataArray, "myLat "+val) + dataArray = append(dataArray, " ") + dataArray = append(dataArray, " #Log") + dataArray = append(dataArray, "20/5/23") + dataArray = append(dataArray, "40m cw 0950 ik5zve/5 9 5") + + temporaryDataFileName := createTestFile(dataArray) + + //When + loadedLogFile, isLoadedOK := LoadFile(temporaryDataFileName, true) + + //Then + if isLoadedOK { + t.Error("Test file processing should return with an error") + } + if len(loadedLogFile) == 0 { + t.Error("No data loaded") + } + + expectedValue := "*" + val + if loadedLogFile[0].MyLat != expectedValue { + t.Errorf("Not the expected MyLat value: %s (expecting %s)", loadedLogFile[0].MyLat, expectedValue) + } + + //Clean Up + os.Remove(temporaryDataFileName) +} + +func TestLoadFile_outofrange_high_myLat_must_fail(t *testing.T) { + Outofrange_myLatHelper(t, "90.0001") +} + +func TestLoadFile_outofrange_low_myLat_must_fail(t *testing.T) { + Outofrange_myLatHelper(t, "-90.0001") +} + func TestLoadFile_redefining_myCall_must_fail(t *testing.T) { //Given @@ -1074,6 +1244,8 @@ func TestLoadFile_wrongData(t *testing.T) { dataArray = append(dataArray, "myWwff foobar") dataArray = append(dataArray, "mySota foobar") dataArray = append(dataArray, "myGrid foobar") + dataArray = append(dataArray, "myLat foobar") + dataArray = append(dataArray, "myLon foobar") dataArray = append(dataArray, " ") dataArray = append(dataArray, " #Log") dataArray = append(dataArray, "date 2020-05-23") @@ -1114,6 +1286,12 @@ func TestLoadFile_wrongData(t *testing.T) { if loadedLogFile[0].MyGrid != expectedValue { t.Errorf("Not the expected MyGrid value: %s (expecting %s)", loadedLogFile[0].MyGrid, expectedValue) } + if loadedLogFile[0].MyLat != expectedValue { + t.Errorf("Not the expected MyLat value: %s (expecting %s)", loadedLogFile[0].MyLat, expectedValue) + } + if loadedLogFile[0].MyLon != expectedValue { + t.Errorf("Not the expected MyLon value: %s (expecting %s)", loadedLogFile[0].MyLon, expectedValue) + } expectedValue = "IK5ZVE" if loadedLogFile[0].Call != expectedValue { diff --git a/fleprocess/parse_line.go b/fleprocess/parse_line.go index 8aa6077..6480277 100644 --- a/fleprocess/parse_line.go +++ b/fleprocess/parse_line.go @@ -35,6 +35,8 @@ type LogLine struct { MyPota string MySota string MyGrid string + MyLat string + MyLon string MyCounty string QslMsgFromHeader string Nickname string diff --git a/fleprocess/validate.go b/fleprocess/validate.go index fea9ab2..9e3e4dd 100644 --- a/fleprocess/validate.go +++ b/fleprocess/validate.go @@ -19,10 +19,35 @@ limitations under the License. import ( "fmt" "regexp" + "strconv" "strings" "time" ) +// ValidateLat checks if value is within range of +-90 degrees inclusive. +func ValidateLat(lat string) (ref, errorMsg string) { + if val, err := strconv.ParseFloat(lat, 64); err == nil { + if val <= float64(90.0) && val >= float64(-90.0) { + return lat, "" + } + } + + errorMsg = "[" + lat + "] is an invalid lat" + return "*" + lat, errorMsg +} + +// ValidateLon checks if value is within range of +-180 degrees inclusive. +func ValidateLon(lon string) (ref, errorMsg string) { + if val, err := strconv.ParseFloat(lon, 64); err == nil { + if val <= float64(180.0) && val >= float64(-180.0) { + return lon, "" + } + } + + errorMsg = "[" + lon + "] is an invalid lon" + return "*" + lon, errorMsg +} + // ValidateSota verifies whether the supplied string is a valid SOTA reference. // The syntax is: AA/NN-CCC: Association/Name-3-digit numeric Code (e.g. G/CE-001). func ValidateSota(inputStr string) (ref, errorMsg string) { diff --git a/go.mod b/go.mod index ee221ac..b74dc25 100644 --- a/go.mod +++ b/go.mod @@ -6,15 +6,18 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 + github.com/stretchr/testify v1.9.0 ) require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/go.sum b/go.sum index 4d38587..dd5ee13 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= @@ -22,6 +23,7 @@ github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6 github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= @@ -46,8 +48,9 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=