summaryrefslogtreecommitdiffstats
path: root/scripts/t/Dpkg_Control
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scripts/t/Dpkg_Control.t148
-rw-r--r--scripts/t/Dpkg_Control/bogus-armor-double.dsc13
-rw-r--r--scripts/t/Dpkg_Control/bogus-armor-formfeed.dsc19
-rw-r--r--scripts/t/Dpkg_Control/bogus-armor-inline.dsc9
-rw-r--r--scripts/t/Dpkg_Control/bogus-armor-nested.dsc15
-rw-r--r--scripts/t/Dpkg_Control/bogus-armor-no-sig.dsc4
-rw-r--r--scripts/t/Dpkg_Control/bogus-armor-spaces.dsc18
-rw-r--r--scripts/t/Dpkg_Control/bogus-armor-trail.dsc14
-rw-r--r--scripts/t/Dpkg_Control/bogus-unsigned.dsc5
-rw-r--r--scripts/t/Dpkg_Control/control-130
-rw-r--r--scripts/t/Dpkg_Control_Fields.t540
-rw-r--r--scripts/t/Dpkg_Control_Tests.t71
-rw-r--r--scripts/t/Dpkg_Control_Tests/tests-missing-fields7
-rw-r--r--scripts/t/Dpkg_Control_Tests/tests-plain-text6
-rw-r--r--scripts/t/Dpkg_Control_Tests/tests-valid18
15 files changed, 917 insertions, 0 deletions
diff --git a/scripts/t/Dpkg_Control.t b/scripts/t/Dpkg_Control.t
new file mode 100644
index 0000000..d1ffa89
--- /dev/null
+++ b/scripts/t/Dpkg_Control.t
@@ -0,0 +1,148 @@
+#!/usr/bin/perl
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs :paths);
+
+BEGIN {
+ plan tests => 24;
+
+ use_ok('Dpkg::Control');
+ use_ok('Dpkg::Control::Info');
+}
+
+my $datadir = test_get_data_path();
+
+sub parse_ctrl {
+ my ($type, $path) = @_;
+
+ my $ctrl = Dpkg::Control->new(type => $type);
+ eval {
+ $ctrl->load($path);
+ 1;
+ } or return;
+
+ return $ctrl;
+}
+
+sub parse_dsc {
+ my $path = shift;
+
+ return parse_ctrl(CTRL_PKG_SRC, $path);
+}
+
+my $c = Dpkg::Control::Info->new("$datadir/control-1");
+
+my $io_data;
+my $io;
+
+open $io, '>', \$io_data or die "canno open io string\n";;
+
+$c->output($io);
+my $expected = 'Source: mysource
+Empty-Field:
+Long-Field: line1
+ line 2 line 2 line 2
+ .
+ line 3 line 3 line 3
+ .
+ ..
+ line 4
+My-Field-One: myvalue1
+My-Field-Two: myvalue2
+Numeric-Field: 0
+
+Package: mypackage1
+Architecture: any
+Depends: libc6
+
+Package: mypackage2
+Architecture: all
+Depends: hello
+
+Package: mypackage3
+Architecture: all
+Depends: hello
+Description: short one
+ long one
+ very long one
+';
+is($io_data, $expected, "Dump of $datadir/control-1");
+
+my $src = $c->get_source();
+is($src, $c->[0], 'array representation of Dpkg::Control::Info 1/2');
+is($src->{'numeric-field'}, '0', 'Numeric 0 value parsed correctly');
+is($src->{'my-field-one'}, 'myvalue1', 'Access field through badly capitalized field name');
+is($src->{'long-field'},
+'line1
+line 2 line 2 line 2
+
+ line 3 line 3 line 3
+
+.
+line 4', 'Get multi-line field');
+is($src->{'Empty-field'}, '', 'Get empty field');
+
+my $pkg = $c->get_pkg_by_idx(1);
+is($pkg, $c->[1], 'array representation of Dpkg::Control::Info 2/2');
+is($pkg->{package}, 'mypackage1', 'Name of first package');
+
+$pkg = $c->get_pkg_by_name('mypackage3');
+is($pkg->{package}, 'mypackage3', 'Name of third package');
+is($pkg->{Depends}, 'hello', 'Name of third package');
+
+$pkg = $c->get_pkg_by_idx(2);
+open $io, '>', \$io_data or die "canno open io string\n";;
+$pkg->output($io);
+
+is($io_data,
+'Package: mypackage2
+Architecture: all
+Depends: hello
+', "Dump of second binary package of $datadir/control-1");
+
+# Check OpenPGP armored signatures in source control files
+
+my $dsc;
+
+$dsc = parse_dsc("$datadir/bogus-unsigned.dsc");
+is($dsc, undef, 'Unsigned .dsc w/ OpenPGP armor');
+
+$dsc = parse_dsc("$datadir/bogus-armor-no-sig.dsc");
+is($dsc, undef, 'Signed .dsc w/ OpenPGP armor missing signature');
+
+$dsc = parse_dsc("$datadir/bogus-armor-trail.dsc");
+is($dsc, undef, 'Signed .dsc w/ bogus OpenPGP armor trailer');
+
+$dsc = parse_dsc("$datadir/bogus-armor-inline.dsc");
+is($dsc, undef, 'Signed .dsc w/ bogus OpenPGP inline armor');
+
+$dsc = parse_dsc("$datadir/bogus-armor-formfeed.dsc");
+is($dsc, undef, 'Signed .dsc w/ bogus OpenPGP armor line');
+
+$dsc = parse_dsc("$datadir/bogus-armor-double.dsc");
+ok(defined $dsc, 'Signed .dsc w/ two OpenPGP armor signatures');
+is($dsc->{Source}, 'pass', 'Signed spaced .dsc package name');
+
+$dsc = parse_dsc("$datadir/bogus-armor-spaces.dsc");
+ok(defined $dsc, 'Signed .dsc w/ spaced OpenPGP armor');
+is($dsc->{Source}, 'pass', 'Signed spaced .dsc package name');
+
+$dsc = parse_dsc("$datadir/bogus-armor-nested.dsc");
+ok(defined $dsc, 'Signed .dsc w/ nested OpenPGP armor');
+is($dsc->{Source}, 'pass', 'Signed nested .dsc package name');
diff --git a/scripts/t/Dpkg_Control/bogus-armor-double.dsc b/scripts/t/Dpkg_Control/bogus-armor-double.dsc
new file mode 100644
index 0000000..1888a00
--- /dev/null
+++ b/scripts/t/Dpkg_Control/bogus-armor-double.dsc
@@ -0,0 +1,13 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+Source: pass
+
+-----BEGIN PGP SIGNATURE-----
+
+Valid signature here.
+-----END PGP SIGNATURE-----
+-----BEGIN PGP SIGNATURE-----
+
+Fake signature here.
+-----END PGP SIGNATURE-----
diff --git a/scripts/t/Dpkg_Control/bogus-armor-formfeed.dsc b/scripts/t/Dpkg_Control/bogus-armor-formfeed.dsc
new file mode 100644
index 0000000..70aab18
--- /dev/null
+++ b/scripts/t/Dpkg_Control/bogus-armor-formfeed.dsc
@@ -0,0 +1,19 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Source: fail
+
+-----BEGIN PGP SIGNATURE-----
+Version: vim v7.3.547 (GNU/Linux)
+
+Fake signature here.
+-----END PGP SIGNATURE-----
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+Source: pass
+
+-----BEGIN PGP SIGNATURE
+Version: GnuPG v1.4.12 (GNU/Linux)
+
+Valid signature here.
+-----END PGP SIGNATURE-----
diff --git a/scripts/t/Dpkg_Control/bogus-armor-inline.dsc b/scripts/t/Dpkg_Control/bogus-armor-inline.dsc
new file mode 100644
index 0000000..44942c1
--- /dev/null
+++ b/scripts/t/Dpkg_Control/bogus-armor-inline.dsc
@@ -0,0 +1,9 @@
+Source: fail
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Binary: pass
+
+-----BEGIN PGP SIGNATURE-----
+
+Valid signature here.
+-----END PGP SIGNATURE-----
diff --git a/scripts/t/Dpkg_Control/bogus-armor-nested.dsc b/scripts/t/Dpkg_Control/bogus-armor-nested.dsc
new file mode 100644
index 0000000..ca99c35
--- /dev/null
+++ b/scripts/t/Dpkg_Control/bogus-armor-nested.dsc
@@ -0,0 +1,15 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+Source: pass
+
+-----BEGIN PGP SIGNATURE-----
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+Source: fail
+
+-----BEGIN PGP SIGNATURE-----
+
+Valid signature here.
+-----END PGP SIGNATURE-----
diff --git a/scripts/t/Dpkg_Control/bogus-armor-no-sig.dsc b/scripts/t/Dpkg_Control/bogus-armor-no-sig.dsc
new file mode 100644
index 0000000..4502a18
--- /dev/null
+++ b/scripts/t/Dpkg_Control/bogus-armor-no-sig.dsc
@@ -0,0 +1,4 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+Source: pass
diff --git a/scripts/t/Dpkg_Control/bogus-armor-spaces.dsc b/scripts/t/Dpkg_Control/bogus-armor-spaces.dsc
new file mode 100644
index 0000000..ab71ab5
--- /dev/null
+++ b/scripts/t/Dpkg_Control/bogus-armor-spaces.dsc
@@ -0,0 +1,18 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+Source: pass
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.12 (GNU/Linux)
+
+Valid signature here.
+-----END PGP SIGNATURE-----
+
+Source: fail
+
+-----BEGIN PGP SIGNATURE
+Version: vim v7.3.547 (GNU/Linux)
+
+Fake signature here.
+-----END PGP SIGNATURE
diff --git a/scripts/t/Dpkg_Control/bogus-armor-trail.dsc b/scripts/t/Dpkg_Control/bogus-armor-trail.dsc
new file mode 100644
index 0000000..90b00f1
--- /dev/null
+++ b/scripts/t/Dpkg_Control/bogus-armor-trail.dsc
@@ -0,0 +1,14 @@
+-----BEGIN PGP SIGNED MESSAGE
+
+Source: fail
+
+-----BEGIN PGP SIGNATURE
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Source: pass
+
+-----BEGIN PGP SIGNATURE-----
+
+Valid signature here.
+-----END PGP SIGNATURE-----
diff --git a/scripts/t/Dpkg_Control/bogus-unsigned.dsc b/scripts/t/Dpkg_Control/bogus-unsigned.dsc
new file mode 100644
index 0000000..7573eb3
--- /dev/null
+++ b/scripts/t/Dpkg_Control/bogus-unsigned.dsc
@@ -0,0 +1,5 @@
+-----BEGIN PGP MESSAGE-----
+
+Source: fail
+
+-----END PGP MESSAGE-----
diff --git a/scripts/t/Dpkg_Control/control-1 b/scripts/t/Dpkg_Control/control-1
new file mode 100644
index 0000000..0c70533
--- /dev/null
+++ b/scripts/t/Dpkg_Control/control-1
@@ -0,0 +1,30 @@
+Source: mysource
+# This is a comment
+numeric-field: 0
+my-field-one: myvalue1
+my-field-two: myvalue2
+long-field: line1
+ line 2 line 2 line 2
+ .
+ line 3 line 3 line 3
+ .
+ ..
+ line 4
+empty-field:
+
+# First package
+Package: mypackage1
+Architecture: any
+Depends: libc6
+
+# Second package
+Package: mypackage2
+Architecture: all
+Depends: hello
+
+Package: mypackage3
+Architecture: all
+Depends:hello
+Description: short one
+ long one
+ very long one
diff --git a/scripts/t/Dpkg_Control_Fields.t b/scripts/t/Dpkg_Control_Fields.t
new file mode 100644
index 0000000..1c699e3
--- /dev/null
+++ b/scripts/t/Dpkg_Control_Fields.t
@@ -0,0 +1,540 @@
+#!/usr/bin/perl
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:paths);
+
+BEGIN {
+ plan tests => 2603;
+
+ use_ok('Dpkg::Control::Types');
+ use_ok('Dpkg::Control::FieldsCore');
+ use_ok('Dpkg::Control');
+}
+
+#my $datadir = test_get_data_path();
+
+my @src_dep_fields = qw(
+ Build-Depends
+ Build-Depends-Arch
+ Build-Depends-Indep
+ Build-Conflicts
+ Build-Conflicts-Arch
+ Build-Conflicts-Indep
+);
+my @bin_dep_normal_fields = qw(
+ Pre-Depends
+ Depends
+ Recommends
+ Suggests
+ Enhances
+);
+my @bin_dep_union_fields = qw(
+ Conflicts
+ Breaks
+ Replaces
+ Provides
+ Built-Using
+ Static-Built-Using
+);
+my @bin_dep_fields = (
+ @bin_dep_normal_fields,
+ @bin_dep_union_fields,
+);
+my @src_checksums = qw(
+ Checksums-Md5
+ Checksums-Sha1
+ Checksums-Sha256
+);
+my @bin_checksums = qw(
+ MD5sum
+ SHA1
+ SHA256
+);
+my @src_files = (
+ @src_checksums,
+ qw(
+ Files
+ ),
+);
+my @bin_files = (
+ qw(
+ Filename
+ Size
+ ),
+ @bin_checksums,
+);
+my @vcs_fields = qw(
+ Vcs-Browser
+ Vcs-Arch
+ Vcs-Bzr
+ Vcs-Cvs
+ Vcs-Darcs
+ Vcs-Git
+ Vcs-Hg
+ Vcs-Mtn
+ Vcs-Svn
+);
+my @test_fields = qw(
+ Testsuite
+ Testsuite-Triggers
+);
+
+my %fields = (
+ CTRL_INFO_SRC() => {
+ name => 'debian/control source stanza',
+ fields => [
+ qw(
+ Source
+ Section
+ Priority
+ Maintainer
+ Uploaders
+ Origin
+ Bugs
+ ),
+ @vcs_fields,
+ qw(
+ Homepage
+ Standards-Version
+ Rules-Requires-Root
+ ),
+ @src_dep_fields,
+ @test_fields,
+ qw(
+ Description
+ ),
+ ],
+ },
+ CTRL_INFO_PKG() => {
+ name => 'debian/control binary stanza',
+ fields => [
+ qw(
+ Package
+ Package-Type
+ Section
+ Priority
+ Architecture
+ Subarchitecture
+ Multi-Arch
+ Essential
+ Protected
+ Build-Essential
+ Build-Profiles
+ Built-For-Profiles
+ Kernel-Version
+ ),
+ @bin_dep_fields,
+ qw(
+ Homepage
+ Installer-Menu-Item
+ Task
+ Tag
+ Description
+ ),
+ ],
+ },
+ CTRL_PKG_SRC() => {
+ name => '.dsc',
+ fields => [
+ qw(
+ Format
+ Source
+ Binary
+ Architecture
+ Version
+ Origin
+ Maintainer
+ Uploaders
+ Homepage
+ Description
+ Standards-Version
+ ),
+ @vcs_fields,
+ @test_fields,
+ @src_dep_fields,
+ qw(
+ Package-List
+ ),
+ @src_files,
+ ],
+ },
+ CTRL_PKG_DEB() => {
+ name => 'DEBIAN/control',
+ fields => [
+ qw(
+ Package
+ Package-Type
+ Source
+ Version
+ Kernel-Version
+ Built-For-Profiles
+ Auto-Built-Package
+ Architecture
+ Subarchitecture
+ Installer-Menu-Item
+ Build-Essential
+ Essential
+ Protected
+ Origin
+ Bugs
+ Maintainer
+ Installed-Size
+ ),
+ @bin_dep_fields,
+ qw(
+ Section
+ Priority
+ Multi-Arch
+ Homepage
+ Description
+ Tag
+ Task
+ ),
+ ],
+ },
+ CTRL_INDEX_SRC() => {
+ name => 'Sources',
+ fields => [
+ qw(
+ Format
+ Package
+ Binary
+ Architecture
+ Version
+ Priority
+ Section
+ Origin
+ Maintainer
+ Uploaders
+ Homepage
+ Description
+ Standards-Version
+ ),
+ @vcs_fields,
+ @test_fields,
+ @src_dep_fields,
+ qw(
+ Package-List
+ Directory
+ ),
+ @src_files,
+ ],
+ },
+ CTRL_INDEX_PKG() => {
+ name => 'Packages',
+ fields => [
+ qw(
+ Package
+ Package-Type
+ Source
+ Version
+ Kernel-Version
+ Built-For-Profiles
+ Auto-Built-Package
+ Architecture
+ Subarchitecture
+ Installer-Menu-Item
+ Build-Essential
+ Essential
+ Protected
+ Origin
+ Bugs
+ Maintainer
+ Installed-Size
+ ),
+ @bin_dep_fields,
+ @bin_files,
+ qw(
+ Section
+ Priority
+ Multi-Arch
+ Homepage
+ Description
+ Tag
+ Task
+ ),
+ ],
+ },
+ CTRL_REPO_RELEASE() => {
+ name => 'Release',
+ fields => [
+ qw(
+ Origin
+ Label
+ Suite
+ Version
+ Codename
+ Changelogs
+ Date
+ Valid-Until
+ NotAutomatic
+ ButAutomaticUpgrades
+ Acquire-By-Hash
+ No-Support-for-Architecture-all
+ Architectures
+ Components
+ Description
+ ),
+ @bin_checksums,
+ ],
+ },
+ CTRL_CHANGELOG() => {
+ name => 'debian/changelog',
+ fields => [
+ qw(
+ Source
+ Binary-Only
+ Version
+ Distribution
+ Urgency
+ Maintainer
+ Timestamp
+ Date
+ Closes
+ Changes
+ ),
+ ],
+ },
+ CTRL_COPYRIGHT_HEADER() => {
+ name => 'debian/copyright Format stanza',
+ fields => [
+ qw(
+ Format
+ Upstream-Name
+ Upstream-Contact
+ Source
+ Disclaimer
+ Comment
+ License
+ Copyright
+ ),
+ ],
+ },
+ CTRL_COPYRIGHT_FILES() => {
+ name => 'debian/copyright Files stanza',
+ fields => [
+ qw(
+ Files
+ Copyright
+ License
+ Comment
+ ),
+ ],
+ },
+ CTRL_COPYRIGHT_LICENSE() => {
+ name => 'debian/copyright License stanza',
+ fields => [
+ qw(
+ License
+ Comment
+ ),
+ ],
+ },
+ CTRL_TESTS() => {
+ name => 'debian/tests/control',
+ fields => [
+ qw(
+ Test-Command
+ Tests
+ Tests-Directory
+ Architecture
+ Restrictions
+ Features
+ Classes
+ Depends
+ ),
+ ],
+ },
+ CTRL_FILE_BUILDINFO() => {
+ name => '.buildinfo',
+ fields => [
+ qw(
+ Format
+ Source
+ Binary
+ Architecture
+ Version
+ Binary-Only-Changes
+ ),
+ @src_checksums,
+ qw(
+ Build-Origin
+ Build-Architecture
+ Build-Kernel-Version
+ Build-Date
+ Build-Path
+ Build-Tainted-By
+ Installed-Build-Depends
+ Environment
+ ),
+ ],
+ },
+ CTRL_FILE_CHANGES() => {
+ name => '.changes',
+ fields => [
+ qw(
+ Format
+ Date
+ Source
+ Binary
+ Binary-Only
+ Built-For-Profiles
+ Architecture
+ Version
+ Distribution
+ Urgency
+ Maintainer
+ Changed-By
+ Description
+ Closes
+ Changes
+ ),
+ @src_files,
+ ],
+ },
+ CTRL_FILE_VENDOR() => {
+ name => 'dpkg origin',
+ fields => [
+ qw(
+ Vendor
+ Vendor-Url
+ Bugs
+ Parent
+ ),
+ ],
+ },
+ CTRL_FILE_STATUS() => {
+ name => 'dpkg status',
+ fields => [
+ qw(
+ Package
+ Essential
+ Protected
+ Status
+ Priority
+ Section
+ Installed-Size
+ Origin
+ Maintainer
+ Bugs
+ Architecture
+ Multi-Arch
+ Source
+ Version
+ Config-Version
+ Replaces
+ Provides
+ Depends
+ Pre-Depends
+ Recommends
+ Suggests
+ Breaks
+ Conflicts
+ Enhances
+ Conffiles
+ Description
+ Triggers-Pending
+ Triggers-Awaited
+ Auto-Built-Package
+ Build-Essential
+ Built-For-Profiles
+ Built-Using
+ Static-Built-Using
+ Homepage
+ Installer-Menu-Item
+ Kernel-Version
+ Package-Type
+ Subarchitecture
+ Tag
+ Task
+ ),
+ ],
+ },
+);
+
+is_deeply([ field_list_src_dep() ],
+ [ @src_dep_fields ],
+ 'List of build dependencies');
+is_deeply([ field_list_pkg_dep() ],
+ [ @bin_dep_fields ],
+ 'List of build dependencies');
+
+is(field_capitalize('invented-field'), 'Invented-Field',
+ 'Field Invented-Field capitalization');
+ok(!field_is_official('invented-field'),
+ 'Field Invented-Field is not official');
+
+my %known_fields;
+foreach my $type (sort keys %fields) {
+ if (not $fields{$type}->{unordered}) {
+ is_deeply([ field_ordered_list($type) ], $fields{$type}->{fields},
+ "List of $fields{$type}->{name} fields");
+ }
+
+ foreach my $field (@{$fields{$type}->{fields}}) {
+ $known_fields{$field} = 1;
+ }
+}
+
+foreach my $field (sort keys %known_fields) {
+ is(field_capitalize($field), $field, "Field $field capitalization");
+ is(field_capitalize(lc $field), $field, "Field lc($field) capitalization");
+ is(field_capitalize(uc $field), $field, "Field uc($field) capitalization");
+
+ ok(field_is_official($field), "Field $field is official");
+ ok(field_is_official(lc $field), "Field lc($field) is official");
+ ok(field_is_official(uc $field), "Field uc($field) is official");
+}
+
+foreach my $type (sort keys %fields) {
+ my %allowed_fields = map { $_ => 1 } @{$fields{$type}->{fields}};
+
+ foreach my $field (sort keys %known_fields) {
+ if ($allowed_fields{$field}) {
+ ok(field_is_allowed_in($field, $type),
+ "Field $field allowed for type $fields{$type}->{name}");
+ } else {
+ ok(!field_is_allowed_in($field, $type),
+ "Field $field not allowed for type $fields{$type}->{name}");
+ }
+ }
+}
+
+# Check deb822 field parsers
+
+my $ctrl = Dpkg::Control->new(type => CTRL_PKG_DEB);
+
+my ($source, $version);
+
+$ctrl->{Package} = 'test-binary';
+$ctrl->{Version} = '2.0-1';
+$ctrl->{Source} = 'test-source (1.0)';
+($source, $version) = field_parse_binary_source($ctrl);
+is($source, 'test-source', 'Source package from binary w/ Source field');
+is($version, '1.0', 'Source version from binary w/ Source field');
+
+$ctrl->{Source} = 'test-source';
+($source, $version) = field_parse_binary_source($ctrl);
+is($source, 'test-source', 'Source package from binary w/ Source field w/o version');
+is($version, '2.0-1', 'Source version from binary w/ Source field w/o version');
+
+delete $ctrl->{Source};
+($source, $version) = field_parse_binary_source($ctrl);
+is($source, 'test-binary', 'Source package from binary w/o Source field');
+is($version, '2.0-1', 'Source version from binary w/o Source field');
diff --git a/scripts/t/Dpkg_Control_Tests.t b/scripts/t/Dpkg_Control_Tests.t
new file mode 100644
index 0000000..27042dc
--- /dev/null
+++ b/scripts/t/Dpkg_Control_Tests.t
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More tests => 5;
+use Test::Dpkg qw(:paths);
+
+BEGIN {
+ use_ok('Dpkg::Control::Tests');
+}
+
+my $datadir = test_get_data_path();
+
+sub parse_tests {
+ my $path = shift;
+
+ my $tests = Dpkg::Control::Tests->new();
+ eval {
+ $tests->load($path);
+ 1;
+ } or return;
+
+ return $tests;
+}
+
+my $tests;
+
+$tests = parse_tests("$datadir/tests-missing-fields");
+is($tests, undef, 'autopkgtest missing required fields');
+
+$tests = parse_tests("$datadir/tests-plain-text");
+is($tests, undef, 'autopkgtest is not in deb822 format');
+
+my $expected = <<'TESTS';
+Tests: aaa, bbb, ccc
+
+Tests: danger, warning
+Restrictions: rw-build-tree, needs-root, breaks-testbed
+
+Tests: depends
+Depends: @, @builddeps@, extra-package
+
+Tests: dir
+Tests-Directory: .
+
+Tests: feature
+
+Tests: class
+Classes: self-test
+
+Test-Command: command arg1 arg2
+
+TESTS
+
+$tests = parse_tests("$datadir/tests-valid");
+ok(defined $tests, 'Valid autopkgtest control file');
+is($tests->output(), $expected, 'autopkgtest control file dumped');
diff --git a/scripts/t/Dpkg_Control_Tests/tests-missing-fields b/scripts/t/Dpkg_Control_Tests/tests-missing-fields
new file mode 100644
index 0000000..7f7794d
--- /dev/null
+++ b/scripts/t/Dpkg_Control_Tests/tests-missing-fields
@@ -0,0 +1,7 @@
+Tests: aaa
+
+Test-Command: command
+
+Other:
+
+Tests: bbb
diff --git a/scripts/t/Dpkg_Control_Tests/tests-plain-text b/scripts/t/Dpkg_Control_Tests/tests-plain-text
new file mode 100644
index 0000000..a29d388
--- /dev/null
+++ b/scripts/t/Dpkg_Control_Tests/tests-plain-text
@@ -0,0 +1,6 @@
+This is a plain text file
+that does not contain
+
+ any of the expected fields nor values
+
+and should fail to load.
diff --git a/scripts/t/Dpkg_Control_Tests/tests-valid b/scripts/t/Dpkg_Control_Tests/tests-valid
new file mode 100644
index 0000000..c0ad60b
--- /dev/null
+++ b/scripts/t/Dpkg_Control_Tests/tests-valid
@@ -0,0 +1,18 @@
+Tests: aaa, bbb, ccc
+
+Tests: danger, warning
+Restrictions: rw-build-tree, needs-root, breaks-testbed
+
+Tests: depends
+Depends: @, @builddeps@, extra-package
+
+Tests: dir
+Tests-Directory: .
+
+Tests: feature
+Features:
+
+Tests: class
+Classes: self-test
+
+Test-Command: command arg1 arg2