@@ -250,3 +250,152 @@ def test_check_sru_version_string_breaks_upgrades(requests_mock):
250250 ubuntu_lint .check_sru_version_string_breaks_upgrades (
251251 ubuntu_lint .Context (changes = changes_bad_version )
252252 )
253+
254+
255+ def test_check_sru_version_string_convention (requests_mock ):
256+ changelog_tmpl = """hello ({next_version}) noble; urgency=medium
257+
258+ * Fix a bug (LP: #12345678)
259+
260+ -- John Doe <john.doe@example.com> Wed, 11 Mar 2026 16:01:41 -0400
261+
262+ hello ({prev_version}) noble; urgency=high
263+
264+ * No change rebuild for 64-bit time_t and frame pointers.
265+
266+ -- John Doe <john.doe@example.com> Mon, 08 Apr 2024 17:58:52 +0200
267+ """
268+
269+ rmadison_tmpls = [
270+ textwrap .dedent (
271+ """hello | 2.8-4 | trusty | source
272+ hello | 2.10-1 | xenial | source
273+ hello | 2.10-1build1 | bionic | source
274+ hello | 2.10-1build3 | bionic-security | source
275+ hello | 2.10-1build3 | bionic-updates | source
276+ hello | 2.10-2ubuntu2 | focal | source
277+ hello | 2.10-2ubuntu4 | jammy | source
278+ hello | {prev_version}| noble | source
279+ hello | 2.10-5 | questing | source
280+ hello | 2.10-5build1 | resolute | source
281+ """ ),
282+ # Same version in two releases
283+ textwrap .dedent (
284+ """hello | 2.8-4 | trusty | source
285+ hello | 2.10-1 | xenial | source
286+ hello | 2.10-1build1 | bionic | source
287+ hello | 2.10-1build3 | bionic-security | source
288+ hello | 2.10-1build3 | bionic-updates | source
289+ hello | 2.10-2ubuntu2 | focal | source
290+ hello | 2.10-2ubuntu4 | jammy | source
291+ hello | {prev_version}| noble | source
292+ hello | {prev_version}| questing | source
293+ hello | 2.10-5build1 | resolute | source
294+ """ ),
295+ ]
296+
297+ # (prev_version, next_version, expect_pass)
298+ testcases_list = [
299+ [
300+ # 2.10-3 -> "2.10-3ubuntu0.1"
301+ ("2.10-3" , "2.10-3ubuntu0.1" , True ),
302+ ("2.10-3" , "2.10-3ubuntu1" , False ),
303+ ("2.10-3" , "2.10-4" , False ),
304+ # 2.10-3ubuntu0.1 -> "2.10-3ubuntu0.2"
305+ ("2.10-3ubuntu0.1" , "2.10-3ubuntu0.2" , True ),
306+ ("2.10-3ubuntu0.1" , "2.10-3ubuntu1" , False ),
307+ # 2.10-3ubuntu2 -> "2.10-3ubuntu2.1"
308+ ("2.10-3ubuntu2" , "2.10-3ubuntu2.1" , True ),
309+ ("2.10-3ubuntu2" , "2.10-3ubuntu3" , False ),
310+ # 2.10-3ubuntu2.1 -> "2.10-3ubuntu2.2"
311+ ("2.10-3ubuntu2.1" , "2.10-3ubuntu2.2" , True ),
312+ ("2.10-3ubuntu2.1" , "2.10-3ubuntu3" , False ),
313+ # 2.10-3build1 -> 2.10-3ubuntu0.1
314+ ("2.10-3build1" , "2.10-3ubuntu0.1" , True ),
315+ ("2.10-3build1" , "2.10-3build2" , False ),
316+ ("2.10-3build1" , "2.10-3ubuntu1" , False ),
317+ # 2.10-3ubuntu0.24.04.1 -> 2.10-3ubuntu0.24.04.2
318+ ("2.10-3ubuntu0.24.04.1" , "2.10-3ubuntu0.24.04.2" , True ),
319+ ("2.10-3ubuntu0.24.04.1" , "2.10-3ubuntu1" , False ),
320+ ],
321+ [
322+ # 2.10-5 in two releases -> 2.10-5ubuntu0.24.04.1
323+ ("2.10-5" , "2.10-5ubuntu0.24.04.1" , True ),
324+ ("2.10-5" , "2.10-5ubuntu0.1" , False ),
325+ ("2.10-5" , "2.10-5ubuntu1" , False ),
326+ ("2.10-5" , "2.10-6" , False ),
327+ # 2.10-5build1 in two releases -> 2.10-5ubuntu0.24.04.1
328+ ("2.10-5build1" , "2.10-5ubuntu0.24.04.1" , True ),
329+ ("2.10-5build1" , "2.10-5ubuntu0.1" , False ),
330+ ("2.10-5build1" , "2.10-5ubuntu1" , False ),
331+ ("2.10-5build1" , "2.10-5build2" , False ),
332+ # 2.10-5ubuntu1 in two releases -> 2.10-5ubuntu1.24.04.1
333+ ("2.10-5ubuntu1" , "2.10-5ubuntu1.24.04.1" , True ),
334+ ("2.10-5ubuntu1" , "2.10-5ubuntu1.1" , False ),
335+ ("2.10-5ubuntu1" , "2.10-5ubuntu2" , False ),
336+ # 2.10-5ubuntu1.1 in two releases -> 2.10-5ubuntu1.1.24.04.1
337+ ("2.10-5ubuntu1.1" , "2.10-5ubuntu1.1.24.04.1" , True ),
338+ ("2.10-5ubuntu1.1" , "2.10-5ubuntu1.2" , False ),
339+ ("2.10-5ubuntu1.1" , "2.10-5ubuntu2" , False ),
340+ ],
341+ ]
342+
343+ for i in range (len (testcases_list )):
344+ rmadison_tmpl = rmadison_tmpls [i ]
345+ testcases = testcases_list [i ]
346+
347+ for (prev_version , next_version , expect_pass ) in testcases :
348+ requests_mock .get (
349+ "https://people.canonical.com/~ubuntu-archive/madison.cgi?package=hello&a=source&text=on" ,
350+ text = rmadison_tmpl .format (prev_version = prev_version )
351+ )
352+ debian_changelog = changelog .Changelog (
353+ changelog_tmpl .format (
354+ prev_version = prev_version ,
355+ next_version = next_version ,
356+ )
357+ )
358+ context = ubuntu_lint .Context (debian_changelog = debian_changelog )
359+
360+ if expect_pass :
361+ ubuntu_lint .check_sru_version_string_convention (context )
362+ else :
363+ with pytest .raises (
364+ ubuntu_lint .LintFailure ,
365+ match = f"{ next_version } does not match expected version"
366+ ):
367+ ubuntu_lint .check_sru_version_string_convention (context )
368+
369+ # Test cases for new upstream version
370+ testcases = [
371+ # 2.10-3ubuntu1 -> 2.11-0ubuntu0.24.04.1 (new upstream)
372+ ("2.10-3ubuntu1" , "2.11-0ubuntu0.24.04.1" , True ),
373+ ("2.10-3ubuntu1" , "2.11-0ubuntu0.1" , False ),
374+ # 2.10-3ubuntu1 -> 2.11-0ubuntu0~24.04.1 (new upstream)
375+ ("2.10-3ubuntu1" , "2.11-0ubuntu0~24.04.1" , True ),
376+ # 2.10-3ubuntu1 -> 2.11-1ubuntu2~24.04.1 (devel backport)
377+ ("2.10-3ubuntu1" , "2.11-1ubuntu2~24.04.1" , True ),
378+ ("2.10-3ubuntu1" , "2.11-1ubuntu2" , False ),
379+ ]
380+
381+ for (prev_version , next_version , expect_pass ) in testcases :
382+ requests_mock .get (
383+ "https://people.canonical.com/~ubuntu-archive/madison.cgi?package=hello&a=source&text=on" ,
384+ text = rmadison_tmpls [0 ].format (prev_version = prev_version )
385+ )
386+ debian_changelog = changelog .Changelog (
387+ changelog_tmpl .format (
388+ prev_version = prev_version ,
389+ next_version = next_version ,
390+ )
391+ )
392+ context = ubuntu_lint .Context (debian_changelog = debian_changelog )
393+
394+ if expect_pass :
395+ ubuntu_lint .check_sru_version_string_convention (context )
396+ else :
397+ with pytest .raises (
398+ ubuntu_lint .LintFailure ,
399+ match = "version string for new upstream should contain suffix"
400+ ):
401+ ubuntu_lint .check_sru_version_string_convention (context )
0 commit comments