1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
= Atom Feeds For Branches =
Atom feeds produce XML not HTML. Therefore we must parse the output as XML
using BeautifulStoneSoup instead of BeautifulSoup or the helper functions.
>>> from BeautifulSoup import BeautifulStoneSoup as BSS
>>> from BeautifulSoup import SoupStrainer
>>> from canonical.launchpad.ftests.feeds_helper import (
... parse_ids, parse_links, validate_feed)
== Create some specific branches to use for this test ==
>>> login(ANONYMOUS)
>>> from lp.testing import time_counter
>>> from datetime import datetime, timedelta
>>> import pytz
>>> date_generator = time_counter(
... datetime(2007, 12, 1, tzinfo=pytz.UTC), timedelta(days=1))
>>> def make_branch(owner, product, name):
... global factory, date_generator
... factory.makeProductBranch(name=name, product=product,
... owner=owner, date_created=date_generator.next())
>>> mike = factory.makePerson(name='mike', displayname='Mike Murphy')
>>> mary = factory.makePerson(name='mary', displayname='Mary Murphy')
>>> project = factory.makeProject(name='oh-man', displayname='Oh Man')
>>> product1 = factory.makeProduct(
... name='fooix', project=project, displayname="Fooix")
>>> product2 = factory.makeProduct(
... name='fooey', project=project, displayname="Fooey")
>>> make_branch(mike, product1, 'first')
>>> make_branch(mike, product2, 'second')
>>> make_branch(mike, product1, 'third')
>>> make_branch(mike, product2, 'fourth')
>>> make_branch(mary, product1, 'fifth')
>>> make_branch(mary, product2, 'sixth')
>>> from canonical.database.sqlbase import flush_database_updates
>>> flush_database_updates()
>>> logout()
== Feed for a person's branches ==
The feed for a person's branches will show the most recent 25 branches
which will include an entry for each branch.
>>> anon_browser.open('http://feeds.launchpad.dev/~mike/branches.atom')
>>> def validate_browser_feed(browser):
... validate_feed(
... browser.contents, browser.headers['content-type'], browser.url)
>>> validate_browser_feed(anon_browser)
No Errors
>>> BSS(anon_browser.contents).title.contents
[u'Branches for Mike Murphy']
>>> def print_parse_ids(browser):
... for id in parse_ids(browser.contents):
... print id
Ignore the date associated with the id of 'mike' as this is the date created
of the person, which will be different each time the test is run.
>>> print_parse_ids(anon_browser)
<id>tag:launchpad.net,...:/code/~mike</id>
<id>tag:launchpad.net,2007-12-04:/code/~mike/fooey/fourth</id>
<id>tag:launchpad.net,2007-12-03:/code/~mike/fooix/third</id>
<id>tag:launchpad.net,2007-12-02:/code/~mike/fooey/second</id>
<id>tag:launchpad.net,2007-12-01:/code/~mike/fooix/first</id>
Ensure the self link is correct and there is only one.
>>> def print_parse_links(browser):
... for link in parse_links(browser.contents, rel="self"):
... print link
>>> print_parse_links(anon_browser)
<link rel="self" href="http://feeds.launchpad.dev/~mike/branches.atom" />
The <update> field for the feed will be the most recent value for the
updated field in all of the entries.
>>> strainer = SoupStrainer('updated')
>>> updated_dates = [extract_text(tag) for tag in BSS(anon_browser.contents,
... parseOnlyThese=strainer)]
>>> feed_updated = updated_dates[0]
>>> entry_dates = sorted(updated_dates[1:], reverse=True)
>>> assert feed_updated == entry_dates[0], (
... "Feed <update> value is not the same as latest entry.")
If an anonymous user fetches the same feed the email addresses will
still be hidden:
>>> anon_browser.open('http://feeds.launchpad.dev/~name12/branches.atom')
>>> validate_browser_feed(anon_browser)
No Errors
>>> BSS(anon_browser.contents).title.contents
[u'Branches for Sample Person']
>>> 'foo@localhost' in anon_browser.contents
False
>>> 'email address hidden' in anon_browser.contents
True
If a branch is marked private it will not be displayed. The Landscape
developers team has two branches which are both private.
>>> from zope.component import getUtility
>>> from zope.security.proxy import removeSecurityProxy
>>> from lp.code.model.branch import Branch
>>> from lp.code.interfaces.branchcollection import (
... IAllBranches)
>>> from lp.registry.interfaces.person import IPersonSet
>>> login(ANONYMOUS)
>>> person_set = getUtility(IPersonSet)
>>> landscape_team = person_set.getByName('landscape-developers')
>>> test_user = person_set.getByEmail('test@canonical.com')
>>> branches = getUtility(IAllBranches).relatedTo(landscape_team)
>>> branches = branches.visibleByUser(
... test_user).getBranches().order_by(Branch.id)
>>> for branch in branches:
... branch = removeSecurityProxy(branch)
... print branch.unique_name, branch.private
~landscape-developers/landscape/trunk True
~name12/landscape/feature-x True
>>> logout()
If we look at the feed for landscape developers there will be no
branches listed, just an id for the feed.
>>> browser.open('http://feeds.launchpad.dev/~landscape-developers/branches.atom')
>>> validate_browser_feed(browser)
No Errors
>>> BSS(browser.contents).title.contents
[u'Branches for Landscape Developers']
>>> print_parse_ids(browser)
<id>tag:launchpad.net,2006-07-11:/code/~landscape-developers</id>
== Feed for a product's branches ==
The feed for a product's branches will show the most recent 25 branches
which will include an entry for each branch.
>>> anon_browser.open('http://feeds.launchpad.dev/fooix/branches.atom')
>>> validate_browser_feed(anon_browser)
No Errors
>>> BSS(anon_browser.contents).title.contents
[u'Branches for Fooix']
>>> print_parse_ids(anon_browser)
<id>tag:launchpad.net,...:/code/fooix</id>
<id>tag:launchpad.net,2007-12-05:/code/~mary/fooix/fifth</id>
<id>tag:launchpad.net,2007-12-03:/code/~mike/fooix/third</id>
<id>tag:launchpad.net,2007-12-01:/code/~mike/fooix/first</id>
>>> print_parse_links(anon_browser)
<link rel="self" href="http://feeds.launchpad.dev/fooix/branches.atom" />
The <update> field for the feed will be the most recent value for the
updated field in all of the entries.
>>> strainer = SoupStrainer('updated')
>>> updated_dates = [extract_text(tag) for tag in BSS(anon_browser.contents,
... parseOnlyThese=strainer)]
>>> feed_updated = updated_dates[0]
>>> entry_dates = sorted(updated_dates[1:], reverse=True)
>>> assert feed_updated == entry_dates[0], (
... "Feed <update> value is not the same as latest entry.")
== Feed for a project's branches ==
The feed for a project's branches will show the most recent 25 branches
which will include an entry for each branch.
>>> anon_browser.open('http://feeds.launchpad.dev/oh-man/branches.atom')
>>> validate_browser_feed(anon_browser)
No Errors
>>> BSS(anon_browser.contents).title.contents
[u'Branches for Oh Man']
>>> print_parse_ids(anon_browser)
<id>tag:launchpad.net,...:/code/oh-man</id>
<id>tag:launchpad.net,2007-12-06:/code/~mary/fooey/sixth</id>
<id>tag:launchpad.net,2007-12-05:/code/~mary/fooix/fifth</id>
<id>tag:launchpad.net,2007-12-04:/code/~mike/fooey/fourth</id>
<id>tag:launchpad.net,2007-12-03:/code/~mike/fooix/third</id>
<id>tag:launchpad.net,2007-12-02:/code/~mike/fooey/second</id>
<id>tag:launchpad.net,2007-12-01:/code/~mike/fooix/first</id>
>>> print_parse_links(anon_browser)
<link rel="self" href="http://feeds.launchpad.dev/oh-man/branches.atom" />
The <update> field for the feed will be the most recent value for the
updated field in all of the entries.
>>> strainer = SoupStrainer('updated')
>>> updated_dates = [extract_text(tag) for tag in BSS(anon_browser.contents,
... parseOnlyThese=strainer)]
>>> feed_updated = updated_dates[0]
>>> entry_dates = sorted(updated_dates[1:], reverse=True)
>>> assert feed_updated == entry_dates[0], (
... "Feed <update> value is not the same as latest entry.")
== Feed for a single branch ==
A single branch can have an Atom feed with each revision being a
different entry.
>>> url = 'http://feeds.launchpad.dev/~mark/firefox/release--0.9.1/branch.atom'
>>> browser.open(url)
>>> validate_feed(browser.contents,
... browser.headers['content-type'], browser.url)
No Errors
>>> BSS(browser.contents).title.contents
[u'Latest Revisions for Branch lp://dev/~mark/firefox/release--0.9.1']
>>> print browser.url
http://feeds.launchpad.dev/~mark/firefox/release--0.9.1/branch.atom
The first <id> in a feed identifies the feed. Each entry then has its
own <id>, which in the case of a single branch feed will be identical.
>>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id'))
>>> ids = parse_ids(browser.contents)
>>> for id_ in ids:
... print id_
<id>tag:launchpad.net,2006-10-16:/code/~mark/firefox/release--0.9.1</id>
<id>tag:launchpad.net,2005-03-09:/code/~mark/firefox/release--0.9.1/revision/1</id>
>>> print_parse_links(browser)
<link rel="self" href="http://feeds.launchpad.dev/~mark/firefox/release--0.9.1/branch.atom" />
>>> strainer = SoupStrainer('updated')
>>> updated_dates = [extract_text(tag) for tag in BSS(browser.contents,
... parseOnlyThese=strainer)]
The update date for the entire feed (updated_dates[0]) must be equal
to the update_date of the first entry in the feed (updated_dates[1]).
>>> updated_dates[0] == updated_dates[1]
True
|