Skip to content

Commit

Permalink
opml: add newsblur test case, add fallback time formats
Browse files Browse the repository at this point in the history
Signed-off-by: VirtualTam <virtualtam@flibidi.net>
  • Loading branch information
virtualtam committed Nov 7, 2024
1 parent 97c8425 commit 817e827
Show file tree
Hide file tree
Showing 7 changed files with 342 additions and 153 deletions.
113 changes: 113 additions & 0 deletions marshal_feedreader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (c) VirtualTam
// SPDX-License-Identifier: MIT

package opml

import (
"encoding/xml"
"path/filepath"
"testing"
)

var (
feedReaderDocumentNewsblur = Document{
XMLName: xml.Name{Local: "opml"},
Version: Version1_1,
Head: Head{
Title: "NewsBlur Feeds",
DateCreated: mustDecodeRFC1123Time("Thu, 07 Nov 2024 20:18:01.109756 GMT"),
DateModified: mustDecodeRFC1123Time("Thu, 07 Nov 2024 20:18:01.109756 GMT"),
},
Body: Body{
Outlines: []Outline{
{
Text: "Security",
Title: "Security",
Outlines: []Outline{
{
Text: "Google Online Security Blog",
Title: "Google Online Security Blog",
Type: OutlineTypeSubscription,
Version: RSSVersion1,
HtmlUrl: "http://security.googleblog.com/",
XmlUrl: "http://www.blogger.com/feeds/1176949257541686127/posts/default?max-results=25&redirect=false&start-index=26",
},
{
Text: "Blog on Library",
Title: "Blog on Library",
Type: OutlineTypeSubscription,
Version: RSSVersion1,
HtmlUrl: "https://www.openssl.org/blog/",
XmlUrl: "https://openssl-library.org:443/post/atom.xml",
},
{
Text: "Schneier on Security",
Title: "Schneier on Security",
Type: OutlineTypeSubscription,
Version: RSSVersion1,
HtmlUrl: "https://www.schneier.com",
XmlUrl: "https://www.schneier.com/feed/atom/",
},
},
},
{
Text: "Self-Hosted",
Title: "Self-Hosted",
},
{
Text: "Cryptography",
Title: "Cryptography",
},
{
Text: "Programming",
Title: "Programming",
Outlines: []Outline{
{
Text: "Git Rev News",
Title: "Git Rev News",
Type: OutlineTypeSubscription,
Version: RSSVersion1,
HtmlUrl: "https://git.github.io/rev_news/",
XmlUrl: "https://git.github.io/feed.xml",
},
{
Text: "The Go Programming Language Blog",
Title: "The Go Programming Language Blog",
Type: OutlineTypeSubscription,
Version: RSSVersion1,
HtmlUrl: "tag:blog.golang.org,2013:blog.golang.org",
XmlUrl: "https://go.dev/blog/feed.atom",
},
},
},
},
},
}
)

func TestUnmarshalFeedReader(t *testing.T) {
cases := []struct {
tname string
inputFileName string
want Document
}{
{
tname: "newsblur",
inputFileName: "newsblur.opml",
want: feedReaderDocumentNewsblur,
},
}

for _, tc := range cases {
t.Run(tc.tname, func(t *testing.T) {
inputFilePath := filepath.Join("testdata", "feedreader", tc.inputFileName)

got, err := UnmarshalFile(inputFilePath)
if err != nil {
t.Fatalf("want no error, got %q", err)
}

assertDocumentsEqual(t, *got, tc.want)
})
}
}
162 changes: 21 additions & 141 deletions marshal_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"os"
"path/filepath"
"testing"
"time"
)

var (
Expand All @@ -19,7 +18,7 @@ var (
Version: Version2,
Head: Head{
Title: "Illustrating the category attribute",
DateCreated: mustParseTimeGMT("Mon, 31 Oct 2005 19:23:00 GMT"),
DateCreated: mustDecodeRFC1123Time("Mon, 31 Oct 2005 19:23:00 GMT"),
},
Body: Body{
Outlines: []Outline{
Expand All @@ -29,7 +28,7 @@ var (
"/Philosophy/Baseball/Mets",
"/Tourism/New York",
},
Created: mustParseTimeGMT("Mon, 31 Oct 2005 18:21:33 GMT"),
Created: mustDecodeRFC1123Time("Mon, 31 Oct 2005 18:21:33 GMT"),
},
},
},
Expand All @@ -42,8 +41,8 @@ var (
Version: Version2,
Head: Head{
Title: "scriptingNewsDirectory.opml",
DateCreated: mustParseTimeGMT("Thu, 13 Oct 2005 15:34:07 GMT"),
DateModified: mustParseTimeGMT("Tue, 25 Oct 2005 21:33:57 GMT"),
DateCreated: mustDecodeRFC1123Time("Thu, 13 Oct 2005 15:34:07 GMT"),
DateModified: mustDecodeRFC1123Time("Tue, 25 Oct 2005 21:33:57 GMT"),
OwnerName: "Dave Winer",
OwnerEmail: "dwiner@yahoo.com",
VertScrollState: 1,
Expand All @@ -56,19 +55,19 @@ var (
Outlines: []Outline{
{
Text: "Scripting News sites",
Created: mustParseTimeGMT("Sun, 16 Oct 2005 05:56:10 GMT"),
Created: mustDecodeRFC1123Time("Sun, 16 Oct 2005 05:56:10 GMT"),
Type: OutlineTypeLink,
Url: "http://hosting.opml.org/dave/mySites.opml",
},
{
Text: "News.Com top 100 OPML",
Created: mustParseTimeGMT("Tue, 25 Oct 2005 21:33:28 GMT"),
Created: mustDecodeRFC1123Time("Tue, 25 Oct 2005 21:33:28 GMT"),
Type: OutlineTypeLink,
Url: "http://news.com.com/html/ne/blogs/CNETNewsBlog100.opml",
},
{
Text: "BloggerCon III Blogroll",
Created: mustParseTimeGMT("Mon, 24 Oct 2005 05:23:52 GMT"),
Created: mustDecodeRFC1123Time("Mon, 24 Oct 2005 05:23:52 GMT"),
Type: OutlineTypeLink,
Url: "http://static.bloggercon.org/iii/blogroll.opml",
},
Expand All @@ -89,13 +88,13 @@ var (
},
{
Text: "Memeorandum",
Created: mustParseTimeGMT("Thu, 13 Oct 2005 15:19:05 GMT"),
Created: mustDecodeRFC1123Time("Thu, 13 Oct 2005 15:19:05 GMT"),
Type: OutlineTypeLink,
Url: "http://tech.memeorandum.com/index.opml",
},
{
Text: "DaveNet archive",
Created: mustParseTimeGMT("Wed, 12 Oct 2005 01:39:56 GMT"),
Created: mustDecodeRFC1123Time("Wed, 12 Oct 2005 01:39:56 GMT"),
Type: OutlineTypeLink,
Url: "http://davenet.opml.org/index.opml",
},
Expand All @@ -110,8 +109,8 @@ var (
Version: Version2,
Head: Head{
Title: "placesLived.opml",
DateCreated: mustParseTimeGMT("Mon, 27 Feb 2006 12:09:48 GMT"),
DateModified: mustParseTimeGMT("Mon, 27 Feb 2006 12:11:44 GMT"),
DateCreated: mustDecodeRFC1123Time("Mon, 27 Feb 2006 12:09:48 GMT"),
DateModified: mustDecodeRFC1123Time("Mon, 27 Feb 2006 12:11:44 GMT"),
OwnerName: "Dave Winer",
ExpansionState: []int{1, 2, 5, 10, 13, 15},
VertScrollState: 1,
Expand Down Expand Up @@ -180,8 +179,8 @@ var (
Version: Version2,
Head: Head{
Title: "workspace.userlandsamples.doSomeUpstreaming",
DateCreated: mustParseTimeGMT("Mon, 11 Feb 2002 22:48:02 GMT"),
DateModified: mustParseTimeGMT("Sun, 30 Oct 2005 03:30:17 GMT"),
DateCreated: mustDecodeRFC1123Time("Mon, 11 Feb 2002 22:48:02 GMT"),
DateModified: mustDecodeRFC1123Time("Sun, 30 Oct 2005 03:30:17 GMT"),
OwnerName: "Dave Winer",
OwnerEmail: "dwiner@yahoo.com",
ExpansionState: []int{1, 2, 4},
Expand Down Expand Up @@ -250,8 +249,8 @@ var (
Version: Version2,
Head: Head{
Title: "states.opml",
DateCreated: mustParseTimeGMT("Tue, 15 Mar 2005 16:35:45 GMT"),
DateModified: mustParseTimeGMT("Thu, 14 Jul 2005 23:41:05 GMT"),
DateCreated: mustDecodeRFC1123Time("Tue, 15 Mar 2005 16:35:45 GMT"),
DateModified: mustDecodeRFC1123Time("Thu, 14 Jul 2005 23:41:05 GMT"),
OwnerName: "Dave Winer",
OwnerEmail: "dave@scripting.com",
ExpansionState: []int{1, 6, 13, 16, 18, 20},
Expand All @@ -277,19 +276,19 @@ var (
Outlines: []Outline{
{
Text: "Reno",
Created: mustParseTimeGMT("Tue, 12 Jul 2005 23:56:35 GMT"),
Created: mustDecodeRFC1123Time("Tue, 12 Jul 2005 23:56:35 GMT"),
},
{
Text: "Las Vegas",
Created: mustParseTimeGMT("Tue, 12 Jul 2005 23:56:37 GMT"),
Created: mustDecodeRFC1123Time("Tue, 12 Jul 2005 23:56:37 GMT"),
},
{
Text: "Ely",
Created: mustParseTimeGMT("Tue, 12 Jul 2005 23:56:39 GMT"),
Created: mustDecodeRFC1123Time("Tue, 12 Jul 2005 23:56:39 GMT"),
},
{
Text: "Gerlach",
Created: mustParseTimeGMT("Tue, 12 Jul 2005 23:56:47 GMT"),
Created: mustDecodeRFC1123Time("Tue, 12 Jul 2005 23:56:47 GMT"),
},
},
},
Expand Down Expand Up @@ -389,8 +388,8 @@ var (
Version: Version2,
Head: Head{
Title: "mySubscriptions.opml",
DateCreated: mustParseTimeGMT("Sat, 18 Jun 2005 12:11:52 GMT"),
DateModified: mustParseTimeGMT("Tue, 02 Aug 2005 21:42:48 GMT"),
DateCreated: mustDecodeRFC1123Time("Sat, 18 Jun 2005 12:11:52 GMT"),
DateModified: mustDecodeRFC1123Time("Tue, 02 Aug 2005 21:42:48 GMT"),
OwnerName: "Dave Winer",
OwnerEmail: "dave@scripting.com",
VertScrollState: 1,
Expand Down Expand Up @@ -649,122 +648,3 @@ func TestUnmarshalFileSpec(t *testing.T) {
})
}
}

func mustParseTimeGMT(dateStr string) time.Time {
parsed, err := time.ParseInLocation(time.RFC1123, dateStr, locationGMT)
if err != nil {
panic(err)
}

return parsed
}

func assertDocumentsEqual(t *testing.T, got, want Document) {
t.Helper()

// OPML metadata
if got.XMLName.Local != want.XMLName.Local {
t.Errorf("want XMLName.Local %q, got %q", want.XMLName.Local, got.XMLName.Local)
}
if got.Version != want.Version {
t.Errorf("want Version %q, got %q", want.Version, got.Version)
}

// Head
if !got.Head.DateCreated.Equal(want.Head.DateCreated) {
t.Errorf("want Head > DateCreated %q, got %q", want.Head.DateCreated, got.Head.DateCreated)
}
if !got.Head.DateModified.Equal(want.Head.DateModified) {
t.Errorf("want Head > DateModified %q, got %q", want.Head.DateModified, got.Head.DateModified)
}
if got.Head.OwnerName != want.Head.OwnerName {
t.Errorf("want Head > OwnerName %q, got %q", want.Head.OwnerName, got.Head.OwnerName)
}
if got.Head.OwnerEmail != want.Head.OwnerEmail {
t.Errorf("want Head > OwnerEmail %q, got %q", want.Head.OwnerEmail, got.Head.OwnerEmail)
}
if got.Head.VertScrollState != want.Head.VertScrollState {
t.Errorf("want Head > VertScrollState %d, got %d", want.Head.VertScrollState, got.Head.VertScrollState)
}
if got.Head.WindowTop != want.Head.WindowTop {
t.Errorf("want Head > WindowTop %d, got %d", want.Head.WindowTop, got.Head.WindowTop)
}
if got.Head.WindowLeft != want.Head.WindowLeft {
t.Errorf("want Head > WindowLeft %d, got %d", want.Head.WindowLeft, got.Head.WindowLeft)
}
if got.Head.WindowBottom != want.Head.WindowBottom {
t.Errorf("want Head > WindowBottom %d, got %d", want.Head.WindowBottom, got.Head.WindowBottom)
}
if got.Head.WindowRight != want.Head.WindowRight {
t.Errorf("want Head > WindowRight %d, got %d", want.Head.WindowRight, got.Head.WindowRight)
}

// Body
assertOutlinesEqual(t, got.Body.Outlines, want.Body.Outlines)
}

func assertOutlinesEqual(t *testing.T, gotOutlines, wantOutlines []Outline) {
t.Helper()

if len(gotOutlines) != len(wantOutlines) {
t.Fatalf("want %d Outlines, got %d", len(wantOutlines), len(gotOutlines))
}

for index, wantOutline := range wantOutlines {
gotOutline := gotOutlines[index]

if gotOutline.Text != wantOutline.Text {
t.Errorf("want Outline %d Text %q, got %q", index, wantOutline.Text, gotOutline.Text)
}

if len(gotOutline.Categories) != len(wantOutline.Categories) {
t.Errorf("want Outline %d %d Categories, got %d", index, len(wantOutline.Categories), len(gotOutline.Categories))
}

for cIndex, wantCategory := range wantOutline.Categories {
gotCategory := gotOutline.Categories[cIndex]

if gotCategory != wantCategory {
t.Errorf("want Outline %d Category %d %q, got %q", index, cIndex, wantCategory, gotCategory)
}
}

if !gotOutline.Created.Equal(wantOutline.Created) {
t.Errorf("want Outline %d Created %q, got %q", index, wantOutline.Created, gotOutline.Created)
}
if gotOutline.Type != wantOutline.Type {
t.Errorf("want Outline %d Type %q, got %q", index, wantOutline.Type, gotOutline.Type)
}
if gotOutline.Url != wantOutline.Url {
t.Errorf("want Outline %d URL %q, got %q", index, wantOutline.Url, gotOutline.Url)
}

if gotOutline.Version != wantOutline.Version {
t.Errorf("want Outline %d Version %q, got %q", index, wantOutline.Version, gotOutline.Version)
}
if gotOutline.Title != wantOutline.Title {
t.Errorf("want Outline %d Title %q, got %q", index, wantOutline.Title, gotOutline.Title)
}
if gotOutline.Description != wantOutline.Description {
t.Errorf("want Outline %d Description %q, got %q", index, wantOutline.Description, gotOutline.Description)
}
if gotOutline.Language != wantOutline.Language {
t.Errorf("want Outline %d Language %q, got %q", index, wantOutline.Language, gotOutline.Language)
}
if gotOutline.HtmlUrl != wantOutline.HtmlUrl {
t.Errorf("want Outline %d HTMLURL %q, got %q", index, wantOutline.HtmlUrl, gotOutline.HtmlUrl)
}
if gotOutline.XmlUrl != wantOutline.XmlUrl {
t.Errorf("want Outline %d XMLURL %q, got %q", index, wantOutline.XmlUrl, gotOutline.XmlUrl)
}

if gotOutline.IsBreakpoint != wantOutline.IsBreakpoint {
t.Errorf("want Outline %d IsBreakpoint %t, got %t", index, wantOutline.IsBreakpoint, gotOutline.IsBreakpoint)
}
if gotOutline.IsComment != wantOutline.IsComment {
t.Errorf("want Outline %d IsComment %t, got %t", index, wantOutline.IsComment, gotOutline.IsComment)
}

assertOutlinesEqual(t, gotOutline.Outlines, wantOutline.Outlines)
}
}
Loading

0 comments on commit 817e827

Please sign in to comment.