diff --git a/clouds/clouds_test.go b/clouds/clouds_test.go index 57348a5..01e1d28 100644 --- a/clouds/clouds_test.go +++ b/clouds/clouds_test.go @@ -1,76 +1,70 @@ package clouds -import "testing" +import ( + "testing" -type testpair struct { - input string - expected Cloud - expectedHeightInM int -} - -var tests = []testpair{ - - // Type CloudType - // height int - // HeightNotDefined bool - // Cumulonimbus bool - // ToweringCumulus bool - // CBNotDefined bool - - {"FEW005", Cloud{FEW, 5, false, false, false, false}, 150}, - {"FEW010CB", Cloud{FEW, 10, false, true, false, false}, 300}, - {"SCT018", Cloud{SCT, 18, false, false, false, false}, 550}, - {"BKN025///", Cloud{BKN, 25, false, false, false, true}, 760}, - {"OVC///", Cloud{OVC, 0, true, false, false, false}, 0}, - {"///015", Cloud{NotDefined, 15, false, false, false, false}, 460}, - {"//////", Cloud{NotDefined, 0, true, false, false, false}, 0}, - {"//////CB", Cloud{NotDefined, 0, true, true, false, false}, 0}, - {"BKN020TCU", Cloud{BKN, 20, false, false, true, false}, 610}, - {"NSC", Cloud{NSC, 0, false, false, false, false}, 0}, - {"RESHRA", Cloud{NotDefined, 0, false, false, false, false}, 0}, -} + . "github.com/smartystreets/goconvey/convey" + . "github.com/urkk/metar/conversion" +) func TestParseCloud(t *testing.T) { arr := &Clouds{} - for _, pair := range tests { - v, ok := ParseCloud(pair.input) - if ok && v != pair.expected { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", v, - ) - } - if ok && !arr.AppendCloud(pair.input) { - t.Error("For", pair.input, "error append cloud") - } else if !ok && arr.AppendCloud(pair.input) { - t.Error("For", pair.input, "error append cloud") - } - } -} -func TestHeightFt(t *testing.T) { - for _, pair := range tests { - v, ok := ParseCloud(pair.input) - if ok && v.HeightFt() != pair.expected.height*100 { - t.Error( - "For", pair.input, - "expected", pair.expected.height*100, - "got", v.HeightFt(), - ) - } + type testpair struct { + input string + expected Cloud } -} -func TestHeightM(t *testing.T) { - for _, pair := range tests { - v, ok := ParseCloud(pair.input) - if ok && v.HeightM() != pair.expectedHeightInM { - t.Error( - "For", pair.input, - "expected", pair.expectedHeightInM, - "got", v.HeightM(), - ) - } + var tests = []testpair{ + + // Type CloudType + // height int + // HeightNotDefined bool + // Cumulonimbus bool + // ToweringCumulus bool + // CBNotDefined bool + + {"FEW005", Cloud{FEW, 5, false, false, false, false}}, + {"FEW010CB", Cloud{FEW, 10, false, true, false, false}}, + {"SCT018", Cloud{SCT, 18, false, false, false, false}}, + {"BKN025///", Cloud{BKN, 25, false, false, false, true}}, + {"OVC///", Cloud{OVC, 0, true, false, false, false}}, + {"///015", Cloud{NotDefined, 15, false, false, false, false}}, + {"//////", Cloud{NotDefined, 0, true, false, false, false}}, + {"//////CB", Cloud{NotDefined, 0, true, true, false, false}}, + {"BKN020TCU", Cloud{BKN, 20, false, false, true, false}}, + {"NSC", Cloud{NSC, 0, false, false, false, false}}, + {"RESHRA", Cloud{"", 0, false, false, false, false}}, } + Convey("Cloud layer parsing tests", t, func() { + Convey("cloud must parsed correctly", func() { + for _, pair := range tests { + cloud, _ := ParseCloud(pair.input) + So(cloud, ShouldResemble, pair.expected) + + } + }) + + Convey("height in feet must calculated correctly", func() { + for _, pair := range tests { + cloud, _ := ParseCloud(pair.input) + So(cloud.HeightFt(), ShouldEqual, pair.expected.height*100) + } + }) + + Convey("height in meters must calculated correctly", func() { + for _, pair := range tests { + cloud, _ := ParseCloud(pair.input) + So(cloud.HeightM(), ShouldEqual, FtToM(pair.expected.height*100)) + } + }) + + Convey("correct cloud must can be appended", func() { + for _, pair := range tests { + _, ok := ParseCloud(pair.input) + So(arr.AppendCloud(pair.input), ShouldEqual, ok) + } + }) + + }) } diff --git a/conversion/conversion.go b/conversion/conversion.go index 6e93e77..cfc5ac5 100644 --- a/conversion/conversion.go +++ b/conversion/conversion.go @@ -20,8 +20,8 @@ func MpsToKts(m float64) float64 { } // SMileToM - converts statute miles to meters -func SMileToM(sm int) int { - return int(math.Round(float64(sm) * 1609.344)) +func SMileToM(sm float64) int { + return int(math.Round(sm * 1609.344)) } // FtToM - converts feet to meters (rounded to 10 meters) diff --git a/conversion/conversion_test.go b/conversion/conversion_test.go index 7d45549..f4135ec 100644 --- a/conversion/conversion_test.go +++ b/conversion/conversion_test.go @@ -1,97 +1,115 @@ package conversion -import "testing" +import ( + "math" + "testing" -type testpairint struct { - input int - expected int -} - -var testsHPaToMmHg = []testpairint{ - {1020, 765}, //765.0650305276 - {1015, 761}, //761.3147117505 - {1011, 758}, //758.3144567288 -} + . "github.com/smartystreets/goconvey/convey" +) func TestHPaToMmHg(t *testing.T) { - for _, pair := range testsHPaToMmHg { - v := HPaToMmHg(pair.input) - if v != pair.expected { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", v, - ) + Convey("converts hectopascal to mm of mercury", t, func() { + for input := 985; input < 1027; input++ { + So(HPaToMmHg(input), ShouldAlmostEqual, float64(input)*0.75006375541921, 1) } - } + }) } -var testsMmHgToHPa = []testpairint{ - {768, 1024}, //1023.91296 - {764, 1019}, //1018.58008 - {758, 1011}, //1010.58076 +func TestMmHgToHPa(t *testing.T) { + Convey("converts mm of mercury to hectopascal", t, func() { + for input := 740; input < 780; input++ { + So(MmHgToHPa(input), ShouldAlmostEqual, float64(input)*1.333223684, 1) + } + }) } -func TestMmHgToHPa(t *testing.T) { - for _, pair := range testsMmHgToHPa { - v := MmHgToHPa(pair.input) - if v != pair.expected { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", v, - ) +func TestDirectionToCardinalDirection(t *testing.T) { + + type testpairDirection struct { + input int + expected string + } + + var testsDirection = []testpairDirection{ + {360, "N"}, + {30, "NE"}, + {275, "W"}, + {0, "N"}, + } + + Convey("converts direction in degrees to points of the compass", t, func() { + for _, pair := range testsDirection { + So(DirectionToCardinalDirection(pair.input), ShouldEqual, pair.expected) } + }) +} + +func TestCalcRelativeHumidity(t *testing.T) { + type testRelativeHumidity struct { + temp, dewpoint, rh int + } + var testsRelativeHumidity = []testRelativeHumidity{ + {20, 7, 43}, + {31, 7, 22}, + {25, 25, 100}, + {25, 19, 69}, + {-7, -13, 62}, + {0, -9, 51}, } + Convey("calculates the relative humidity of the dew point and temperature", t, func() { + for _, data := range testsRelativeHumidity { + So(CalcRelativeHumidity(data.temp, data.dewpoint), ShouldEqual, data.rh) + } + }) } -type testpairDirection struct { - input int - expected string +func TestKphToMps(t *testing.T) { + Convey("converts kilometres per hour to meters per second", t, func() { + for input := .0; input < 200; input++ { + So(KphToMps(input), ShouldAlmostEqual, input/3.6, .00001) + } + }) } -var testsDirection = []testpairDirection{ - {360, "N"}, - {30, "NE"}, - {275, "W"}, - {0, "N"}, +func TestKtsToMps(t *testing.T) { + Convey("converts knots to meters per second", t, func() { + for input := .0; input < 50; input++ { + So(KtsToMps(input), ShouldAlmostEqual, input/1.94384, .00001) + } + }) } -func TestDirectionToCardinalDirection(t *testing.T) { - for _, pair := range testsDirection { - v := DirectionToCardinalDirection(pair.input) - if v != pair.expected { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", v, - ) +func TestMpsToKts(t *testing.T) { + Convey("converts meters per second to knots", t, func() { + for input := .0; input < 50; input++ { + So(MpsToKts(input), ShouldAlmostEqual, input*1.94384, .00001) } - } + }) } -type testRelativeHumidity struct { - temp, dewpoint, rh int +func TestSMileToM(t *testing.T) { + Convey("converts statute miles to meters", t, func() { + for input := .0; input < 6.25; { + So(SMileToM(input), ShouldAlmostEqual, input*1609.344, 1) + input += .25 + } + }) } -var testsRelativeHumidity = []testRelativeHumidity{ - {20, 7, 43}, - {31, 7, 22}, - {25, 25, 100}, - {25, 19, 69}, - {-7, -13, 62}, - {0, -9, 51}, +func TestFtToM(t *testing.T) { + Convey("converts feet to meters (rounded to 10 meters)", t, func() { + for input := 300; input < 3000; { + So(FtToM(input), ShouldAlmostEqual, int(math.Round(float64(input)*0.3048/10)*10), 10) + input += 300 + } + }) } -func TestCalcRelativeHumidity(t *testing.T) { - for _, data := range testsRelativeHumidity { - rh := CalcRelativeHumidity(data.temp, data.dewpoint) - if rh != data.rh { - t.Error( - "For t", data.temp, " dew point ", data.dewpoint, - "expected", data.rh, - "got", rh, - ) +func TestInHgTohPa(t *testing.T) { + Convey("converts inch of mercury to hectopascal", t, func() { + for input := 29.0; input < 30.3; { + So(InHgTohPa(input), ShouldAlmostEqual, input*33.86389, 1) + input += .1 } - } + }) } diff --git a/metar_test.go b/metar_test.go index 8bfa23e..c2e040b 100644 --- a/metar_test.go +++ b/metar_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + . "github.com/smartystreets/goconvey/convey" "github.com/urkk/metar/clouds" . "github.com/urkk/metar/phenomena" "github.com/urkk/metar/runways" @@ -15,42 +16,61 @@ var curYear = time.Now().Year() var curMonth = time.Now().Month() var curDay = time.Now().Day() -type visibilityparsetest struct { - input []string - expected *Visibility - tokens int -} +func TestParseVisibility(t *testing.T) { -var visibilityparsetests = []visibilityparsetest{ - // Distance int - // LowerDistance int - // LowerDirection string + type testpair struct { + input []string + expected *Visibility + } - {[]string{"2000"}, &Visibility{2000, 0, ""}, 1}, - {[]string{"3000", "1500NE"}, &Visibility{3000, 1500, "NE"}, 2}, - {[]string{"1500", "1000S"}, &Visibility{1500, 1000, "S"}, 2}, - {[]string{"9999"}, &Visibility{9999, 0, ""}, 1}, - {[]string{"20008MPS"}, &Visibility{0, 0, ""}, 0}, -} + var onetokenpair = []testpair{ + {[]string{"2000"}, &Visibility{2000, 0, ""}}, + {[]string{"9999"}, &Visibility{9999, 0, ""}}, + {[]string{"5500"}, &Visibility{5500, 0, ""}}, + } -func TestParseVisibility(t *testing.T) { - for _, pair := range visibilityparsetests { - vis := &Visibility{} - tokensused := vis.ParseVisibility(pair.input) + var twotokenpair = []testpair{ + {[]string{"3000", "1500NE"}, &Visibility{3000, 1500, "NE"}}, + {[]string{"1500", "1000S"}, &Visibility{1500, 1000, "S"}}, + {[]string{"7000", "5000W"}, &Visibility{7000, 5000, "W"}}, + } - if tokensused > 0 && !reflect.DeepEqual(vis, pair.expected) || tokensused != pair.tokens { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", vis, - ) - } + var incorrecttokenpair = []testpair{ + {[]string{"20008MPS"}, &Visibility{0, 0, ""}}, + {[]string{"OVC020"}, &Visibility{0, 0, ""}}, } -} -type remarksparsetest struct { - input []string - expected Remark + Convey("Prevailing visibility parsing tests", t, func() { + vis := &Visibility{} + Convey("One token testing", func() { + var t int + for _, pair := range onetokenpair { + t = vis.ParseVisibility(pair.input) + So(vis, ShouldResemble, pair.expected) + So(t, ShouldEqual, 1) + } + }) + + Convey("Two token testing", func() { + var t int + for _, pair := range twotokenpair { + t = vis.ParseVisibility(pair.input) + So(vis, ShouldResemble, pair.expected) + So(t, ShouldEqual, 2) + } + }) + + Convey("Incorrect token testing", func() { + var t int + for _, pair := range incorrecttokenpair { + t = vis.ParseVisibility(pair.input) + So(vis, ShouldResemble, pair.expected) + So(t, ShouldEqual, 0) + } + }) + + }) + } func getWind(inp string) wind.Wind { @@ -59,37 +79,6 @@ func getWind(inp string) wind.Wind { return *w } -var remarksparsetests = []remarksparsetest{ - // WindOnRWY []WindOnRWY - // QBB int // cloud base in meters - // МТOBSC bool // Mountains obscured - // MASTOBSC bool // Mast obscured - // OBSTOBSC bool // Obstacle obscured - // QFE int // Q-code Field Elevation (mmHg/hPa) - - {[]string{"RMK", "R06/25002MPS", "QFE762"}, Remark{WindOnRWY: []WindOnRWY{WindOnRWY{Runway: "06", Wind: getWind("25002MPS")}}, QBB: 0, МТOBSC: false, MASTOBSC: false, OBSTOBSC: false, QFE: 762}}, - {[]string{"QBB200", "MT", "OBSC", "QFE762"}, Remark{WindOnRWY: nil, QBB: 200, МТOBSC: true, MASTOBSC: false, OBSTOBSC: false, QFE: 762}}, - {[]string{"QBB180"}, Remark{WindOnRWY: nil, QBB: 180, МТOBSC: false, MASTOBSC: false, OBSTOBSC: false, QFE: 0}}, - {[]string{"MT", "OBSC", "MAST", "OBSC", "OBST", "OBSC", "QFE762/1004"}, Remark{WindOnRWY: nil, QBB: 0, МТOBSC: true, MASTOBSC: true, OBSTOBSC: true, QFE: 762}}, - {[]string{"MT", "OBSC"}, Remark{WindOnRWY: nil, QBB: 0, МТOBSC: true, MASTOBSC: false, OBSTOBSC: false, QFE: 0}}, - {[]string{"MAST", "OBSC"}, Remark{WindOnRWY: nil, QBB: 0, МТOBSC: false, MASTOBSC: true, OBSTOBSC: false, QFE: 0}}, - {[]string{"OBST", "OBSC"}, Remark{WindOnRWY: nil, QBB: 0, МТOBSC: false, MASTOBSC: false, OBSTOBSC: true, QFE: 0}}, - {[]string{"RMK", "R06/25002MPS", "120V180"}, Remark{WindOnRWY: []WindOnRWY{WindOnRWY{Runway: "06", Wind: getWind("25002MPS 120V180")}}, QBB: 0, МТOBSC: false, MASTOBSC: false, OBSTOBSC: false, QFE: 0}}, -} - -func TestParseRemarks(t *testing.T) { - for _, pair := range remarksparsetests { - r := *parseRemarks(pair.input) - if !reflect.DeepEqual(r, pair.expected) { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", r, - ) - } - } -} - type metarparsetest struct { input string expected *MetarMessage diff --git a/phenomena/phenomena_test.go b/phenomena/phenomena_test.go index 4cedee0..9faca92 100644 --- a/phenomena/phenomena_test.go +++ b/phenomena/phenomena_test.go @@ -1,84 +1,74 @@ package phenomena -import "testing" +import ( + "testing" -type testpair struct { - input string - expected *Phenomenon - correct bool -} - -var tests = []testpair{ + . "github.com/smartystreets/goconvey/convey" +) - // Vicinity bool - // Intensity Intensity - // Abbreviation string +func TestParsePhenomena(t *testing.T) { + arr := &Phenomena{} + type testpair struct { + input string + expected *Phenomenon + } - {"PLRADZ", &Phenomenon{false, Moderate, "PLRADZ"}, true}, - {"+SHRASNGS", &Phenomenon{false, Heavy, "SHRASNGS"}, true}, - {"VCBLDU", &Phenomenon{true, Moderate, "BLDU"}, true}, - // not can VC - {"VCSNDZ", &Phenomenon{true, Moderate, "VCSNDZ"}, false}, - {"SHGRRA", &Phenomenon{false, Moderate, "SHGRRA"}, true}, - // "Light" not be applicable - {"-FC", &Phenomenon{false, Light, "FC"}, false}, - {"-PLDZ", &Phenomenon{false, Light, "PLDZ"}, true}, -} + var tests = []testpair{ + // Vicinity bool + // Intensity Intensity + // Abbreviation string + {"PLRADZ", &Phenomenon{false, Moderate, "PLRADZ"}}, + {"+SHRASNGS", &Phenomenon{false, Heavy, "SHRASNGS"}}, + {"VCBLDU", &Phenomenon{true, Moderate, "BLDU"}}, + // not can VC + {"VCSNDZ", nil}, + {"SHGRRA", &Phenomenon{false, Moderate, "SHGRRA"}}, + // "Light" not be applicable + {"-FC", nil}, + {"-PLDZ", &Phenomenon{false, Light, "PLDZ"}}, + } -var recenttests = []testpair{ + Convey("Phenomena parsing tests", t, func() { + Convey("Phenomena must parsed correctly", func() { + for _, pair := range tests { + So(ParsePhenomena(pair.input), ShouldResemble, pair.expected) + } + }) - // Vicinity bool - // Intensity Intensity - // Abbreviation string + Convey("Correct phenomena must can be appended", func() { + for _, pair := range tests { + So(arr.AppendPhenomena(pair.input), ShouldResemble, ParsePhenomena(pair.input) != nil) + } + }) + }) - {"REFZDZ", &Phenomenon{false, Moderate, "FZDZ"}, true}, - {"+REFZDZ", &Phenomenon{false, Heavy, "FZDZ"}, false}, - {"RERASN", &Phenomenon{false, Moderate, "RASN"}, true}, } -func TestParsePhenomena(t *testing.T) { +func TestParseRecentPhenomena(t *testing.T) { arr := &Phenomena{} - for _, pair := range tests { - ph := ParsePhenomena(pair.input) - if pair.correct { - if *ph != *pair.expected { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", ph, - ) - } - if !arr.AppendPhenomena(pair.input) { - t.Error("For", pair.input, "error append phenomenon") - } - } else if !pair.correct { - if ph != nil || arr.AppendPhenomena(pair.input) { - t.Error("false positive at " + pair.input) - } - } + type testpair struct { + input string + expected *Phenomenon + } + var recenttests = []testpair{ + {"REFZDZ", &Phenomenon{false, Moderate, "FZDZ"}}, + {"+REFZDZ", nil}, // + not applicable in recent weather + {"RERASN", &Phenomenon{false, Moderate, "RASN"}}, } -} -func TestParseRecentPhenomena(t *testing.T) { - arr := &Phenomena{} - for _, pair := range recenttests { - ph := ParseRecentPhenomena(pair.input) - if pair.correct { - if *ph != *pair.expected { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", ph, - ) - } - if !arr.AppendRecentPhenomena(pair.input) { - t.Error("For", pair.input, "error append phenomenon") + Convey("Recent phenomena parsing tests", t, func() { + Convey("Recent phenomena must parsed correctly", func() { + for _, pair := range recenttests { + So(ParseRecentPhenomena(pair.input), ShouldResemble, pair.expected) } - } else if !pair.correct { - if ph != nil || arr.AppendRecentPhenomena(pair.input) { - t.Error("false positive at " + pair.input) + }) + + Convey("Correct recent phenomena must can be appended", func() { + for _, pair := range recenttests { + So(arr.AppendRecentPhenomena(pair.input), ShouldResemble, ParseRecentPhenomena(pair.input) != nil) } - } - } + }) + + }) } diff --git a/runways/runways_test.go b/runways/runways_test.go index 9bc2d07..a8ecd20 100644 --- a/runways/runways_test.go +++ b/runways/runways_test.go @@ -1,89 +1,88 @@ package runways -import "testing" +import ( + "testing" -type testpairRVR struct { - input string - expected VisualRange -} - -var testsVisibility = []testpairRVR{ - {"R25/M0075", VisualRange{RunwayDesignator{"25", false}, 75, false, true, NotDefined}}, - {"R33L/P1500", VisualRange{RunwayDesignator{"33L", false}, 1500, true, false, NotDefined}}, - {"R16R/1000U", VisualRange{RunwayDesignator{"16R", false}, 1000, false, false, U}}, - {"R33C/0900N", VisualRange{RunwayDesignator{"33C", false}, 900, false, false, N}}, - {"OVC350", VisualRange{RunwayDesignator{"0", false}, 0, false, false, NotDefined}}, -} + . "github.com/smartystreets/goconvey/convey" +) func TestParseVisibility(t *testing.T) { - for _, pair := range testsVisibility { - v, ok := ParseVisibility(pair.input) - if ok && v != pair.expected { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", v, - ) - } + type testpair struct { + input string + expected VisualRange } -} - -type testpairstate struct { - input string - expected State -} -var testsState = []testpairstate{ + var tests = []testpair{ + {"R25/M0075", VisualRange{RunwayDesignator{"25", false}, 75, false, true, NotDefined}}, + {"R33L/P1500", VisualRange{RunwayDesignator{"33L", false}, 1500, true, false, NotDefined}}, + {"R16R/1000U", VisualRange{RunwayDesignator{"16R", false}, 1000, false, false, U}}, + //artificial situation + {"R88/1000D", VisualRange{RunwayDesignator{"88", true}, 1000, false, false, D}}, + {"R33C/0900N", VisualRange{RunwayDesignator{"33C", false}, 900, false, false, N}}, + {"OVC350", VisualRange{RunwayDesignator{"", false}, 0, false, false, NotDefined}}, + } - {"R25/CLRD70", State{Designator: RunwayDesignator{"25", false}, - BrakingConditions: 70, - CLRD: true}}, - {"R24L/451293", State{Designator: RunwayDesignator{"24L", false}, - TypeOfCoverage: 4, - DimensionOfCoverage: 5, - HeightOfCoverage: 12, - BrakingConditions: 93, - }}, - {"R30/290250", State{Designator: RunwayDesignator{"30", false}, - TypeOfCoverage: 2, - DimensionOfCoverage: 9, - HeightOfCoverage: 2, - BrakingConditions: 50, - }}, - {"R21/0///65", State{Designator: RunwayDesignator{"21", false}, - TypeOfCoverage: 0, - DimensionOfCoverageNotDef: true, - HeightOfCoverageNotDef: true, - BrakingConditions: 65, - }}, - {"R88///////", State{Designator: RunwayDesignator{"88", true}, - TypeOfCoverageNotDef: true, - DimensionOfCoverageNotDef: true, - HeightOfCoverageNotDef: true, - BrakingConditionsNotDefined: true, - }}, - {"R74/4/1293", State{Designator: RunwayDesignator{"24R", false}, - TypeOfCoverage: 4, - DimensionOfCoverageNotDef: true, - HeightOfCoverage: 12, - BrakingConditions: 93, - }}, - {"R31/70D", State{Designator: RunwayDesignator{"31", false}, - BrakingConditions: 70, - CLRD: true, - }}, - {"OVC350", State{Designator: RunwayDesignator{"", false}}}, + Convey("Runway visual range parsing tests", t, func() { + for _, pair := range tests { + vis, _ := ParseVisibility(pair.input) + So(vis, ShouldResemble, pair.expected) + } + }) } func TestParseState(t *testing.T) { - for _, pair := range testsState { - v, ok := ParseState(pair.input) - if ok && v != pair.expected { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", v, - ) - } + + type testpair struct { + input string + expected State + } + + var tests = []testpair{ + + {"R25/CLRD70", State{Designator: RunwayDesignator{"25", false}, + BrakingConditions: 70, + CLRD: true}}, + {"R24L/451293", State{Designator: RunwayDesignator{"24L", false}, + TypeOfCoverage: 4, + DimensionOfCoverage: 5, + HeightOfCoverage: 12, + BrakingConditions: 93, + }}, + {"R30/290250", State{Designator: RunwayDesignator{"30", false}, + TypeOfCoverage: 2, + DimensionOfCoverage: 9, + HeightOfCoverage: 2, + BrakingConditions: 50, + }}, + {"R21/0///65", State{Designator: RunwayDesignator{"21", false}, + TypeOfCoverage: 0, + DimensionOfCoverageNotDef: true, + HeightOfCoverageNotDef: true, + BrakingConditions: 65, + }}, + {"R88///////", State{Designator: RunwayDesignator{"88", true}, + TypeOfCoverageNotDef: true, + DimensionOfCoverageNotDef: true, + HeightOfCoverageNotDef: true, + BrakingConditionsNotDefined: true, + }}, + {"R74/4/1293", State{Designator: RunwayDesignator{"24R", false}, + TypeOfCoverage: 4, + DimensionOfCoverageNotDef: true, + HeightOfCoverage: 12, + BrakingConditions: 93, + }}, + {"R31/70D", State{Designator: RunwayDesignator{"31", false}, + BrakingConditions: 70, + CLRD: true, + }}, + {"OVC350", State{Designator: RunwayDesignator{"", false}}}, } + + Convey("Runway state parsing tests", t, func() { + for _, pair := range tests { + st, _ := ParseState(pair.input) + So(st, ShouldResemble, pair.expected) + } + }) } diff --git a/trend_test.go b/trend_test.go index be750f5..1d33d4c 100644 --- a/trend_test.go +++ b/trend_test.go @@ -1,117 +1,101 @@ package metar import ( - "reflect" "testing" "time" + . "github.com/smartystreets/goconvey/convey" "github.com/urkk/metar/clouds" "github.com/urkk/metar/phenomena" ) -type trendparsetest struct { - input []string - expected Trend -} +func TestParseTrendData(t *testing.T) { -var trendparsetests = []trendparsetest{ + Convey("Trends parsing tests", t, func() { + var input []string + var expected *Trend - {[]string{"TEMPO", "FM1230", "TL1330", "18005MPS", "CAVOK"}, - Trend{Type: TEMPO, - Wind: getWind("18005MPS"), - CAVOK: true, - FM: time.Date(curYear, curMonth, curDay, 12, 30, 0, 0, time.UTC), - TL: time.Date(curYear, curMonth, curDay, 13, 30, 0, 0, time.UTC), - }, - }, - {[]string{"TEMPO", "AT1230", "18005MPS", "BKN003"}, - Trend{Type: TEMPO, - Wind: getWind("18005MPS"), - AT: time.Date(curYear, curMonth, curDay, 12, 30, 0, 0, time.UTC), - Clouds: []clouds.Cloud{getCloud("BKN003")}, - }, - }, - {[]string{"TEMPO", "2509/2515", "0500", "FG", "VV003"}, - Trend{Type: TEMPO, - FM: time.Date(curYear, curMonth, 25, 9, 0, 0, 0, time.UTC), - TL: time.Date(curYear, curMonth, 25, 15, 0, 0, 0, time.UTC), - Visibility: Visibility{Distance: 500, LowerDistance: 0, LowerDirection: ""}, - Phenomena: []phenomena.Phenomenon{phenomena.Phenomenon{Vicinity: false, Abbreviation: "FG", Intensity: ""}}, - VerticalVisibility: 300, - }, - }, - {[]string{"TEMPO", "2506/2512", "3100", "-SHRA", "BR", "BKN005", "OVC020CB"}, - Trend{Type: TEMPO, - FM: time.Date(curYear, curMonth, 25, 6, 0, 0, 0, time.UTC), - TL: time.Date(curYear, curMonth, 25, 12, 0, 0, 0, time.UTC), - Visibility: Visibility{Distance: 3100, LowerDistance: 0, LowerDirection: ""}, - Phenomena: []phenomena.Phenomenon{phenomena.Phenomenon{Vicinity: false, Abbreviation: "SHRA", Intensity: "-"}, phenomena.Phenomenon{Vicinity: false, Abbreviation: "BR", Intensity: ""}}, - Clouds: []clouds.Cloud{getCloud("BKN005"), getCloud("OVC020CB")}, - }, - }, - {[]string{"TEMPO", "2509/2515", "0500", "FG", "VV///"}, - Trend{Type: TEMPO, - FM: time.Date(curYear, curMonth, 25, 9, 0, 0, 0, time.UTC), - TL: time.Date(curYear, curMonth, 25, 15, 0, 0, 0, time.UTC), - Visibility: Visibility{Distance: 500, LowerDistance: 0, LowerDirection: ""}, - Phenomena: []phenomena.Phenomenon{phenomena.Phenomenon{Vicinity: false, Abbreviation: "FG", Intensity: ""}}, - VerticalVisibility: 0, - VerticalVisibilityNotDefined: true, - }, - }, - {[]string{"FM241200", "18003MPS", "CAVOK"}, - Trend{Type: FM, - FM: time.Date(curYear, curMonth, 24, 12, 0, 0, 0, time.UTC), - Wind: getWind("18003MPS"), - CAVOK: true, - }, - }, - {[]string{"BECMG", "2405/2407", "09003G08MPS"}, - Trend{Type: BECMG, - FM: time.Date(curYear, curMonth, 24, 5, 0, 0, 0, time.UTC), - TL: time.Date(curYear, curMonth, 24, 7, 0, 0, 0, time.UTC), - Wind: getWind("09003G08MPS"), - }, - }, - {[]string{"BECMG", "FM2200", "TL2400", "BKN015//"}, - Trend{Type: BECMG, - FM: time.Date(curYear, curMonth, curDay, 22, 0, 0, 0, time.UTC), - TL: time.Date(curYear, curMonth, curDay+1, 00, 0, 0, 0, time.UTC), - Clouds: []clouds.Cloud{getCloud("BKN015//")}, - }, - }, - // misspelled time - {[]string{"BECMG", "FM220O", "TL23O0", "BKN015//"}, - Trend{Type: BECMG, - Clouds: []clouds.Cloud{getCloud("BKN015//")}, - }, - }, - // misspelled time - {[]string{"BECMG", "AT220O", "TL2300", "BKN015//"}, - Trend{Type: BECMG, - TL: time.Date(curYear, curMonth, curDay, 23, 0, 0, 0, time.UTC), - Clouds: []clouds.Cloud{getCloud("BKN015//")}, - }, - }, - // clock error - {[]string{"BECMG", "2526/2526", "0500", "FG"}, - Trend{Type: BECMG, - Visibility: Visibility{Distance: 500, LowerDistance: 0, LowerDirection: ""}, - Phenomena: []phenomena.Phenomenon{phenomena.Phenomenon{Vicinity: false, Abbreviation: "FG", Intensity: ""}}, - }, - }, -} + Convey(`test incorrect time, horisontal visibility and phenomena`, func() { + input = []string{"BECMG", "2526/2526", "0500", "FG"} + expected = &Trend{Type: BECMG, + Visibility: Visibility{Distance: 500, LowerDistance: 0, LowerDirection: ""}, + Phenomena: []phenomena.Phenomenon{phenomena.Phenomenon{Vicinity: false, Abbreviation: "FG", Intensity: ""}}, + } + So(parseTrendData(input), ShouldResemble, expected) + }) -func TestParseTrendData(t *testing.T) { + Convey(`test misspelled time and cloud layer`, func() { + input = []string{"BECMG", "AT220O", "TL23O0", "BKN015//"} + expected = &Trend{Type: BECMG, + Clouds: []clouds.Cloud{getCloud("BKN015//")}, + } + So(parseTrendData(input), ShouldResemble, expected) + }) + + Convey(`test for FM time and CAVOK condition`, func() { + input = []string{"FM241200", "18003MPS", "CAVOK"} + expected = &Trend{Type: FM, + FM: time.Date(curYear, curMonth, 24, 12, 0, 0, 0, time.UTC), + Wind: getWind("18003MPS"), + CAVOK: true, + } + So(parseTrendData(input), ShouldResemble, expected) + }) + + Convey(`test for vertical visibility and phenomena`, func() { + input = []string{"TEMPO", "2509/2515", "0500", "FG", "VV003"} + expected = &Trend{Type: TEMPO, + FM: time.Date(curYear, curMonth, 25, 9, 0, 0, 0, time.UTC), + TL: time.Date(curYear, curMonth, 25, 15, 0, 0, 0, time.UTC), + Visibility: Visibility{Distance: 500, LowerDistance: 0, LowerDirection: ""}, + Phenomena: []phenomena.Phenomenon{phenomena.Phenomenon{Vicinity: false, Abbreviation: "FG", Intensity: ""}}, + VerticalVisibility: 300, + } + So(parseTrendData(input), ShouldResemble, expected) + }) + + Convey(`test for phenomena and multiple cloud layers`, func() { + input = []string{"TEMPO", "2506/2512", "3100", "-SHRA", "BR", "BKN005", "OVC020CB"} + expected = &Trend{Type: TEMPO, + FM: time.Date(curYear, curMonth, 25, 6, 0, 0, 0, time.UTC), + TL: time.Date(curYear, curMonth, 25, 12, 0, 0, 0, time.UTC), + Visibility: Visibility{Distance: 3100, LowerDistance: 0, LowerDirection: ""}, + Phenomena: []phenomena.Phenomenon{phenomena.Phenomenon{Vicinity: false, Abbreviation: "SHRA", Intensity: "-"}, phenomena.Phenomenon{Vicinity: false, Abbreviation: "BR", Intensity: ""}}, + Clouds: []clouds.Cloud{getCloud("BKN005"), getCloud("OVC020CB")}, + } + So(parseTrendData(input), ShouldResemble, expected) + }) + + Convey(`test for wind and expected time of changes`, func() { + input = []string{"TEMPO", "FM1230", "TL1330", "18005MPS", "CAVOK"} + expected = &Trend{Type: TEMPO, + Wind: getWind("18005MPS"), + CAVOK: true, + FM: time.Date(curYear, curMonth, curDay, 12, 30, 0, 0, time.UTC), + TL: time.Date(curYear, curMonth, curDay, 13, 30, 0, 0, time.UTC), + } + So(parseTrendData(input), ShouldResemble, expected) + }) + + Convey(`test incorrect FM time and 24 hours at TL-time `, func() { + input = []string{"BECMG", "FM22o0", "TL2400", "BKN015//"} + expected = &Trend{Type: BECMG, + TL: time.Date(curYear, curMonth, curDay+1, 00, 0, 0, 0, time.UTC), + Clouds: []clouds.Cloud{getCloud("BKN015//")}, + } + So(parseTrendData(input), ShouldResemble, expected) + }) + + Convey(`test AT-time`, func() { + input = []string{"TEMPO", "AT1230", "18005MPS", "BKN003"} + expected = &Trend{Type: TEMPO, + Wind: getWind("18005MPS"), + AT: time.Date(curYear, curMonth, curDay, 12, 30, 0, 0, time.UTC), + Clouds: []clouds.Cloud{getCloud("BKN003")}, + } + So(parseTrendData(input), ShouldResemble, expected) + }) + + }) - for _, pair := range trendparsetests { - tr := *parseTrendData(pair.input) - if !reflect.DeepEqual(tr, pair.expected) { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", tr, - ) - } - } } diff --git a/wind/wind_test.go b/wind/wind_test.go index f3c48ac..752e5d2 100644 --- a/wind/wind_test.go +++ b/wind/wind_test.go @@ -1,113 +1,59 @@ package wind import ( - "reflect" "testing" -) - -type parsetest struct { - input string - expected *Wind -} - -var parsetests = []parsetest{ - // Speed int - // WindDirection int - // GustsSpeed int - // Variable bool - // VariableFrom int - // VariableTo int - // Above50MPS bool - {"31005MPS", &Wind{310, 5, 0, false, 0, 0, false}}, - {"31010KPH", &Wind{310, 2.7777777777777777, 0, false, 0, 0, false}}, - {"VRB15MPS", &Wind{0, 15, 0, true, 0, 0, false}}, - {"00000MPS", &Wind{0, 0, 0, false, 0, 0, false}}, - {"240P49MPS", &Wind{240, 49, 0, false, 0, 0, true}}, - {"04008G20MPS", &Wind{40, 8, 20, false, 0, 0, false}}, - {"22003G08MPS 280V350", &Wind{220, 3, 8, false, 280, 350, false}}, - {"14010KT", &Wind{140, 5.144456333854638, 0, false, 0, 0, false}}, - {"BKN020", &Wind{0, 0, 0, false, 0, 0, false}}, -} -type functest struct { - input Wind - expectedKt int - expectedMps int - expectedGustsKt int - expectedGustsMps int -} - -var functests = []functest{ - {Wind{0, 10, 0, false, 0, 0, false}, 19, 10, 0, 0}, - {Wind{0, 0, 0, false, 0, 0, false}, 0, 0, 0, 0}, - {Wind{0, 15, 0, false, 0, 0, false}, 29, 15, 0, 0}, - {Wind{0, 7.33, 0, false, 0, 0, false}, 14, 7, 0, 0}, - {Wind{0, 10, 10, false, 0, 0, false}, 19, 10, 19, 10}, - {Wind{0, 15, 15, false, 0, 0, false}, 29, 15, 29, 15}, -} + . "github.com/smartystreets/goconvey/convey" + . "github.com/urkk/metar/conversion" +) func TestParseWind(t *testing.T) { - for _, pair := range parsetests { - wnd := &Wind{} - tokensused := wnd.ParseWind(pair.input) - if tokensused > 0 && !reflect.DeepEqual(wnd, pair.expected) { - t.Error( - "For", pair.input, - "expected", pair.expected, - "got", wnd, - ) - } - } -} - -func TestSpeedKt(t *testing.T) { - for _, pair := range functests { - vkt := pair.input.SpeedKt() - if vkt != pair.expectedKt { - t.Error( - "For", pair.input, - "expected", pair.expectedKt, - "got", vkt, - ) - } - } -} -func TestSpeedMps(t *testing.T) { - for _, pair := range functests { - vmps := pair.input.SpeedMps() - if vmps != pair.expectedMps { - t.Error( - "For", pair.input, - "expected", pair.expectedMps, - "got", vmps, - ) - } + type testpair struct { + input string + expected *Wind + tokens int } -} -func TestGustsSpeedMps(t *testing.T) { - for _, pair := range functests { - vmps := pair.input.GustsSpeedMps() - if vmps != pair.expectedGustsMps { - t.Error( - "For", pair.input, - "expected", pair.expectedGustsMps, - "got", vmps, - ) - } + var windtests = []testpair{ + {"31005MPS", &Wind{310, 5, 0, false, 0, 0, false}, 1}, + {"31010KPH", &Wind{310, 2.7777777777777777, 0, false, 0, 0, false}, 1}, + {"VRB15MPS", &Wind{0, 15, 0, true, 0, 0, false}, 1}, + {"00000MPS", &Wind{0, 0, 0, false, 0, 0, false}, 1}, + {"240P49MPS", &Wind{240, 49, 0, false, 0, 0, true}, 1}, + {"04008G20MPS", &Wind{40, 8, 20, false, 0, 0, false}, 1}, + {"22003G08MPS 280V350", &Wind{220, 3, 8, false, 280, 350, false}, 2}, + {"14010KT", &Wind{140, 5.144456333854638, 0, false, 0, 0, false}, 1}, + {"BKN020", &Wind{0, 0, 0, false, 0, 0, false}, 0}, } -} -func TestGustsSpeedKt(t *testing.T) { - for _, pair := range functests { - vmps := pair.input.GustsSpeedKt() - if vmps != pair.expectedGustsKt { - t.Error( - "For", pair.input, - "expected", pair.expectedGustsKt, - "got", vmps, - ) - } - } + Convey("Wind parsing tests", t, func() { + Convey("wind must parsed correctly", func() { + for _, pair := range windtests { + wnd := &Wind{} + tokensused := wnd.ParseWind(pair.input) + So(wnd, ShouldResemble, pair.expected) + So(tokensused, ShouldEqual, pair.tokens) + } + }) + + Convey("speed and gusts speed in meters per second must calculated correctly", func() { + for _, pair := range windtests { + wnd := &Wind{} + wnd.ParseWind(pair.input) + So(wnd.SpeedMps(), ShouldAlmostEqual, wnd.speed, .25) + So(wnd.GustsSpeedMps(), ShouldAlmostEqual, wnd.gustsSpeed, .25) + } + }) + + Convey("speed and gusts speed in knots must calculated correctly", func() { + for _, pair := range windtests { + wnd := &Wind{} + wnd.ParseWind(pair.input) + So(wnd.SpeedKt(), ShouldAlmostEqual, MpsToKts(wnd.speed), 1) + So(wnd.GustsSpeedKt(), ShouldAlmostEqual, MpsToKts(wnd.gustsSpeed), 1) + } + }) + + }) }