Scan NWS forecast discussion blocks once
This commit is contained in:
@@ -236,41 +236,82 @@ func TestForecastDiscussionSectionRoleHeadingsParse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractForecastDiscussionSectionStopsAtSlashQualifiedHeading(t *testing.T) {
|
||||
block, ok := extractForecastDiscussionSection([]string{
|
||||
".LONG TERM /Monday through Next Saturday/...",
|
||||
"Long-term prose.",
|
||||
".AVIATION /18Z TAFS/...",
|
||||
"Aviation prose.",
|
||||
}, forecastDiscussionSectionForRole(forecastDiscussionSectionRoleLongTerm))
|
||||
if !ok {
|
||||
t.Fatalf("extractForecastDiscussionSection() found no LONG TERM block")
|
||||
}
|
||||
if block.heading.qualifier != "Monday through Next Saturday" {
|
||||
t.Fatalf("qualifier = %q, want normalized slash qualifier", block.heading.qualifier)
|
||||
}
|
||||
wantBody := []string{"Long-term prose."}
|
||||
if !reflect.DeepEqual(block.body, wantBody) {
|
||||
t.Fatalf("body = %#v, want %#v", block.body, wantBody)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractForecastDiscussionSectionAllowsEmptyBodyBeforeHeading(t *testing.T) {
|
||||
block, ok := extractForecastDiscussionSection([]string{
|
||||
func TestParseForecastDiscussionSectionBlocksRetainsOrderAndEmptyBodies(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
".SHORT TERM... (Tonight)",
|
||||
".LONG TERM... (Tomorrow)",
|
||||
"Long-term prose.",
|
||||
}, forecastDiscussionSectionForRole(forecastDiscussionSectionRoleShortTerm))
|
||||
if !ok {
|
||||
t.Fatalf("extractForecastDiscussionSection() found no SHORT TERM block")
|
||||
})
|
||||
want := []forecastDiscussionSectionBlock{
|
||||
{heading: forecastDiscussionSectionHeading{section: "SHORT TERM", qualifier: "(Tonight)"}},
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "LONG TERM", qualifier: "(Tomorrow)"},
|
||||
body: []string{"Long-term prose."},
|
||||
},
|
||||
}
|
||||
if len(block.body) != 0 {
|
||||
t.Fatalf("body = %#v, want empty body", block.body)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("blocks = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractForecastDiscussionSectionKeepsMalformedHeadingLikeLines(t *testing.T) {
|
||||
block, ok := extractForecastDiscussionSection([]string{
|
||||
func TestParseForecastDiscussionSectionBlocksStopsAtTerminators(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
"Preamble is ignored.",
|
||||
".KEY MESSAGES...",
|
||||
"- First message.",
|
||||
"&&",
|
||||
".SHORT TERM...",
|
||||
"Short-term prose.",
|
||||
".LSX WATCHES/WARNINGS/ADVISORIES...",
|
||||
"Watch text is ignored.",
|
||||
"$$",
|
||||
"WFO LSX",
|
||||
})
|
||||
want := []forecastDiscussionSectionBlock{
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "KEY MESSAGES"},
|
||||
body: []string{"- First message."},
|
||||
},
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "SHORT TERM"},
|
||||
body: []string{"Short-term prose."},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("blocks = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionSectionBlocksUsesGenericHeadingsAsBoundaries(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
".SHORT TERM...",
|
||||
"Short-term prose.",
|
||||
".SYNOPSIS...",
|
||||
"Synopsis prose.",
|
||||
".LONG TERM /Tomorrow/...",
|
||||
"Long-term prose.",
|
||||
})
|
||||
want := []forecastDiscussionSectionBlock{
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "SHORT TERM"},
|
||||
body: []string{"Short-term prose."},
|
||||
},
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "SYNOPSIS"},
|
||||
body: []string{"Synopsis prose."},
|
||||
},
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "LONG TERM", qualifier: "Tomorrow"},
|
||||
body: []string{"Long-term prose."},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("blocks = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionSectionBlocksKeepsMalformedHeadingLikeLines(t *testing.T) {
|
||||
got := parseForecastDiscussionSectionBlocks([]string{
|
||||
".SHORT TERM... (Tonight)",
|
||||
".short term... Lowercase prose stays in the body.",
|
||||
".LONG TERM /Tomorrow/..",
|
||||
@@ -278,18 +319,63 @@ func TestExtractForecastDiscussionSectionKeepsMalformedHeadingLikeLines(t *testi
|
||||
"Expected short-term prose.",
|
||||
".LONG TERM... (Tomorrow)",
|
||||
"Long-term prose.",
|
||||
}, forecastDiscussionSectionForRole(forecastDiscussionSectionRoleShortTerm))
|
||||
if !ok {
|
||||
t.Fatalf("extractForecastDiscussionSection() found no SHORT TERM block")
|
||||
})
|
||||
want := []forecastDiscussionSectionBlock{
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "SHORT TERM", qualifier: "(Tonight)"},
|
||||
body: []string{
|
||||
".short term... Lowercase prose stays in the body.",
|
||||
".LONG TERM /Tomorrow/..",
|
||||
".LONG TERM /Tomorrow/... more",
|
||||
"Expected short-term prose.",
|
||||
},
|
||||
},
|
||||
{
|
||||
heading: forecastDiscussionSectionHeading{section: "LONG TERM", qualifier: "(Tomorrow)"},
|
||||
body: []string{"Long-term prose."},
|
||||
},
|
||||
}
|
||||
wantBody := []string{
|
||||
".short term... Lowercase prose stays in the body.",
|
||||
".LONG TERM /Tomorrow/..",
|
||||
".LONG TERM /Tomorrow/... more",
|
||||
"Expected short-term prose.",
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("blocks = %#v, want %#v", got, want)
|
||||
}
|
||||
if !reflect.DeepEqual(block.body, wantBody) {
|
||||
t.Fatalf("body = %#v, want %#v", block.body, wantBody)
|
||||
}
|
||||
|
||||
func TestParseForecastDiscussionTextKeepsFirstMappedSections(t *testing.T) {
|
||||
text := strings.Join([]string{
|
||||
"AFDLSX",
|
||||
"National Weather Service Saint Louis MO",
|
||||
"224 PM CDT Sat Mar 28 2026",
|
||||
"",
|
||||
".KEY MESSAGES...",
|
||||
"&&",
|
||||
".KEY MESSAGES...",
|
||||
"- Second key message.",
|
||||
"&&",
|
||||
".SHORT TERM... (First short term)",
|
||||
"First short-term prose.",
|
||||
"&&",
|
||||
".SHORT TERM... (Second short term)",
|
||||
"Second short-term prose.",
|
||||
"&&",
|
||||
".LONG TERM... (First long term)",
|
||||
"First long-term prose.",
|
||||
"&&",
|
||||
".LONG TERM... (Second long term)",
|
||||
"Second long-term prose.",
|
||||
}, "\n")
|
||||
|
||||
got, err := ParseForecastDiscussionText(text)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseForecastDiscussionText() error = %v", err)
|
||||
}
|
||||
if len(got.KeyMessages) != 0 {
|
||||
t.Fatalf("KeyMessages = %#v, want empty first block", got.KeyMessages)
|
||||
}
|
||||
if got.ShortTerm == nil || got.ShortTerm.Qualifier != "(First short term)" || got.ShortTerm.Text != "First short-term prose." {
|
||||
t.Fatalf("ShortTerm = %#v, want first section", got.ShortTerm)
|
||||
}
|
||||
if got.LongTerm == nil || got.LongTerm.Qualifier != "(First long term)" || got.LongTerm.Text != "First long-term prose." {
|
||||
t.Fatalf("LongTerm = %#v, want first section", got.LongTerm)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user