diff mbox series

[v2,4/7] tests: Add tests for new functionality

Message ID 20240730210523.313101-5-ahassick@iol.unh.edu
State Changes Requested
Headers show
Series Add support for series dependencies | expand

Commit Message

Adam Hassick July 30, 2024, 9:05 p.m. UTC
v1:
* Add tests for the API and parser changes.
* Add new test mbox files.
* Add new patch series tests.

Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
---
 patchwork/tests/api/test_series.py            |  33 ++-
 .../tests/series/dependency-base-patch.mbox   | 102 +++++++
 .../series/dependency-multi-2.mbox.template   | 110 +++++++
 .../series/dependency-multi.mbox.template     | 109 +++++++
 .../series/dependency-one-cover.mbox.template | 128 +++++++++
 .../dependency-one-first-patch.mbox.template  | 125 ++++++++
 patchwork/tests/test_parser.py                |  52 ++++
 patchwork/tests/test_series.py                | 271 ++++++++++++++++++
 8 files changed, 929 insertions(+), 1 deletion(-)
 create mode 100644 patchwork/tests/series/dependency-base-patch.mbox
 create mode 100644 patchwork/tests/series/dependency-multi-2.mbox.template
 create mode 100644 patchwork/tests/series/dependency-multi.mbox.template
 create mode 100644 patchwork/tests/series/dependency-one-cover.mbox.template
 create mode 100644 patchwork/tests/series/dependency-one-first-patch.mbox.template

Comments

Stephen Finucane Oct. 31, 2024, 10:35 p.m. UTC | #1
On Tue, 2024-07-30 at 17:05 -0400, Adam Hassick wrote:
> v1:
> * Add tests for the API and parser changes.
> * Add new test mbox files.
> * Add new patch series tests.
> 
> Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>

I'm happy with this now.

Reviewed-by: Stephen Finucane <stephen@that.guru>

> ---
>  patchwork/tests/api/test_series.py            |  33 ++-
>  .../tests/series/dependency-base-patch.mbox   | 102 +++++++
>  .../series/dependency-multi-2.mbox.template   | 110 +++++++
>  .../series/dependency-multi.mbox.template     | 109 +++++++
>  .../series/dependency-one-cover.mbox.template | 128 +++++++++
>  .../dependency-one-first-patch.mbox.template  | 125 ++++++++
>  patchwork/tests/test_parser.py                |  52 ++++
>  patchwork/tests/test_series.py                | 271 ++++++++++++++++++
>  8 files changed, 929 insertions(+), 1 deletion(-)
>  create mode 100644 patchwork/tests/series/dependency-base-patch.mbox
>  create mode 100644 patchwork/tests/series/dependency-multi-2.mbox.template
>  create mode 100644 patchwork/tests/series/dependency-multi.mbox.template
>  create mode 100644 patchwork/tests/series/dependency-one-cover.mbox.template
>  create mode 100644 patchwork/tests/series/dependency-one-first-patch.mbox.template
> 
> diff --git a/patchwork/tests/api/test_series.py b/patchwork/tests/api/test_series.py
> index 730678a..86954b0 100644
> --- a/patchwork/tests/api/test_series.py
> +++ b/patchwork/tests/api/test_series.py
> @@ -44,6 +44,20 @@ class TestSeriesAPI(utils.APITestCase):
>          self.assertIn(series_obj.get_mbox_url(), series_json['mbox'])
>          self.assertIn(series_obj.get_absolute_url(), series_json['web_url'])
>  
> +        for dep, item in zip(
> +            series_obj.dependencies.all(), series_json['dependencies']
> +        ):
> +            self.assertIn(
> +                reverse('api-series-detail', kwargs={'pk': dep.id}), item
> +            )
> +
> +        for dep, item in zip(
> +            series_obj.dependents.all(), series_json['dependents']
> +        ):
> +            self.assertIn(
> +                reverse('api-series-detail', kwargs={'pk': dep.id}), item
> +            )
> +
>          # nested fields
>  
>          self.assertEqual(series_obj.project.id, series_json['project']['id'])
> @@ -95,6 +109,21 @@ class TestSeriesAPI(utils.APITestCase):
>          series_rsp = resp.data[0]
>          self.assertSerialized(series, series_rsp)
>  
> +    def test_dependencies(self):
> +        project_obj = create_project(linkname='myproject')
> +        person_obj = create_person(email='test@example.com')
> +        series1 = create_series(project=project_obj, submitter=person_obj)
> +        create_cover(series=series1)
> +        create_patch(series=series1)
> +        series2 = create_series(project=project_obj, submitter=person_obj)
> +        create_cover(series=series2)
> +        create_patch(series=series2)
> +        series1.add_dependencies([series2])
> +        resp = self.client.get(self.api_url())
> +        self.assertEqual(2, len(resp.data))
> +        self.assertSerialized(series2, resp.data[1])
> +        self.assertSerialized(series1, resp.data[0])
> +
>      def test_list_filter_project(self):
>          """Filter series by project."""
>          series = self._create_series()
> @@ -152,7 +181,7 @@ class TestSeriesAPI(utils.APITestCase):
>              create_cover(series=series_obj)
>              create_patch(series=series_obj)
>  
> -        with self.assertNumQueries(6):
> +        with self.assertNumQueries(8):
>              self.client.get(self.api_url())
>  
>      @utils.store_samples('series-detail')
> @@ -175,6 +204,8 @@ class TestSeriesAPI(utils.APITestCase):
>          self.assertNotIn('web_url', resp.data['cover_letter'])
>          self.assertNotIn('mbox', resp.data['cover_letter'])
>          self.assertNotIn('web_url', resp.data['patches'][0])
> +        self.assertNotIn('dependents', resp.data)
> +        self.assertNotIn('dependencies', resp.data)
>  
>      def test_detail_non_existent(self):
>          """Ensure we get a 404 for a non-existent series."""
> diff --git a/patchwork/tests/series/dependency-base-patch.mbox b/patchwork/tests/series/dependency-base-patch.mbox
> new file mode 100644
> index 0000000..e7f9e94
> --- /dev/null
> +++ b/patchwork/tests/series/dependency-base-patch.mbox
> @@ -0,0 +1,102 @@
> +From ahassick@iol.unh.edu Mon Jun 10 21:09:07 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Subject: [PATCH v1 0/2] Add test files for testing
> +Date: Mon, 10 Jun 2024 17:09:07 -0400
> +Message-ID: <20240610210912.161735-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +Add a test program and a makefile.
> +Big surprise here, but this is all for testing.
> +
> +Adam Hassick (2):
> +  Add test program
> +  Add a Makefile
> +
> + Makefile | 9 +++++++++
> + test.c   | 8 ++++++++
> + 2 files changed, 17 insertions(+)
> + create mode 100644 Makefile
> + create mode 100644 test.c
> +
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Mon Jun 10 21:09:09 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Subject: [PATCH v1 1/2] Add test program
> +Date: Mon, 10 Jun 2024 17:09:09 -0400
> +Message-ID: <20240610210912.161735-3-ahassick@iol.unh.edu>
> +In-Reply-To: <20240610210912.161735-1-ahassick@iol.unh.edu>
> +References: <20240610210912.161735-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +
> +
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + test.c | 8 ++++++++
> + 1 file changed, 8 insertions(+)
> + create mode 100644 test.c
> +
> +diff --git a/test.c b/test.c
> +new file mode 100644
> +index 0000000..5096204
> +--- /dev/null
> ++++ b/test.c
> +@@ -0,0 +1,8 @@
> ++
> ++#include <stdio.h>
> ++
> ++int main() {
> ++	printf("HELLOOOOO!!!! Hi there!");
> ++	return 0;
> ++}
> ++
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Mon Jun 10 21:09:11 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Subject: [PATCH v1 2/2] Add a Makefile
> +Date: Mon, 10 Jun 2024 17:09:11 -0400
> +Message-ID: <20240610210912.161735-5-ahassick@iol.unh.edu>
> +In-Reply-To: <20240610210912.161735-1-ahassick@iol.unh.edu>
> +References: <20240610210912.161735-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +How are people supposed to build this without a Makefile?
> +
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + Makefile | 9 +++++++++
> + 1 file changed, 9 insertions(+)
> + create mode 100644 Makefile
> +
> +diff --git a/Makefile b/Makefile
> +new file mode 100644
> +index 0000000..2126a0e
> +--- /dev/null
> ++++ b/Makefile
> +@@ -0,0 +1,9 @@
> ++
> ++CC=gcc
> ++
> ++test: test.c
> ++	$(CC) -O2 -march=native -mtune=native test.c -o test
> ++
> ++test-debug: test.c
> ++	$(CC) -O0 -g test.c -o test
> ++
> +--
> +2.45.2
> diff --git a/patchwork/tests/series/dependency-multi-2.mbox.template b/patchwork/tests/series/dependency-multi-2.mbox.template
> new file mode 100644
> index 0000000..42296f4
> --- /dev/null
> +++ b/patchwork/tests/series/dependency-multi-2.mbox.template
> @@ -0,0 +1,110 @@
> +From ahassick@iol.unh.edu Wed Jun 12 20:40:05 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 0/2] More changes
> +Date: Wed, 12 Jun 2024 16:40:05 -0400
> +Message-ID: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +Tests.
> +
> +Adam Hassick (2):
> +  test: Change the test function
> +  Fix Makefile
> +
> + Makefile |  2 +-
> + test.c   | 10 ++++++++--
> + 2 files changed, 9 insertions(+), 3 deletions(-)
> +
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Wed Jun 12 20:40:06 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 1/2] test: Change the test function
> +Date: Wed, 12 Jun 2024 16:40:06 -0400
> +Message-ID: <20240612204019.364820-2-ahassick@iol.unh.edu>
> +In-Reply-To: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +References: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +I don't like what it did before. Let's make it do something more
> +interesting this time.
> +
> +Depends-on: {depends_token_1}
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + test.c | 10 ++++++++--
> + 1 file changed, 8 insertions(+), 2 deletions(-)
> +
> +diff --git a/test.c b/test.c
> +index f6cd16e..a868311 100644
> +--- a/test.c
> ++++ b/test.c
> +@@ -1,11 +1,17 @@
> +
> + #include <stdio.h>
> ++#include <stdlib.h>
> + #include <string.h>
> +
> + // This function does things.
> + void thingymabob(char *string) {{
> +-	printf("Here's your argument: %s\n", string);
> +-	printf("Here is the length of your argument: %li\n", strlen(string));
> ++	double f = atof(string);
> ++
> ++	if (f > 5.0) {{
> ++		printf("I'm satisfied.\n");
> ++	}} else {{
> ++		fprintf(stderr, "I'm not satisfied.\n");
> ++	}}
> + }}
> +
> + // Entry point.
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Wed Jun 12 20:40:07 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 2/2] Fix Makefile
> +Date: Wed, 12 Jun 2024 16:40:07 -0400
> +Message-ID: <20240612204019.364820-3-ahassick@iol.unh.edu>
> +X-Mailer: git-send-email 2.45.2
> +In-Reply-To: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +References: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +The test-debug target did not generate the correct file.
> +
> +Depends-on: {depends_token_2}
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + Makefile | 2 +-
> + 1 file changed, 1 insertion(+), 1 deletion(-)
> +
> +diff --git a/Makefile b/Makefile
> +index 3583b93..78a2bf0 100644
> +--- a/Makefile
> ++++ b/Makefile
> +@@ -9,5 +9,5 @@ test: test.c
> +	$(CC) -O2 -march=native -mtune=native -Wall -Werror -pedantic test.c -o test
> +
> + test-debug: test.c
> +-	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test
> ++	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test-debug
> +
> +--
> +2.45.2
> diff --git a/patchwork/tests/series/dependency-multi.mbox.template b/patchwork/tests/series/dependency-multi.mbox.template
> new file mode 100644
> index 0000000..40fba59
> --- /dev/null
> +++ b/patchwork/tests/series/dependency-multi.mbox.template
> @@ -0,0 +1,109 @@
> +From ahassick@iol.unh.edu Wed Jun 12 20:40:05 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: ach1062@usnh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 0/2] More changes
> +Date: Wed, 12 Jun 2024 16:40:05 -0400
> +Message-ID: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +Tests.
> +
> +Depends-on: {depends_token_1}
> +Depends-on: {depends_token_2}
> +Adam Hassick (2):
> +  test: Change the test function
> +  Fix Makefile
> +
> + Makefile |  2 +-
> + test.c   | 10 ++++++++--
> + 2 files changed, 9 insertions(+), 3 deletions(-)
> +
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Wed Jun 12 20:40:06 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: ach1062@usnh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 1/2] test: Change the test function
> +Date: Wed, 12 Jun 2024 16:40:06 -0400
> +Message-ID: <20240612204019.364820-2-ahassick@iol.unh.edu>
> +In-Reply-To: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +References: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +I don't like what it did before. Let's make it do something more
> +interesting this time.
> +
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + test.c | 10 ++++++++--
> + 1 file changed, 8 insertions(+), 2 deletions(-)
> +
> +diff --git a/test.c b/test.c
> +index f6cd16e..a868311 100644
> +--- a/test.c
> ++++ b/test.c
> +@@ -1,11 +1,17 @@
> +
> + #include <stdio.h>
> ++#include <stdlib.h>
> + #include <string.h>
> +
> + // This function does things.
> + void thingymabob(char *string) {{
> +-	printf("Here's your argument: %s\n", string);
> +-	printf("Here is the length of your argument: %li\n", strlen(string));
> ++	double f = atof(string);
> ++
> ++	if (f > 5.0) {{
> ++		printf("I'm satisfied.\n");
> ++	}} else {{
> ++		fprintf(stderr, "I'm not satisfied.\n");
> ++	}}
> + }}
> +
> + // Entry point.
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Wed Jun 12 20:40:07 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: ach1062@usnh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 2/2] Fix Makefile
> +Date: Wed, 12 Jun 2024 16:40:07 -0400
> +Message-ID: <20240612204019.364820-3-ahassick@iol.unh.edu>
> +In-Reply-To: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +References: <20240612204019.364820-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +The test-debug target did not generate the correct file.
> +
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + Makefile | 2 +-
> + 1 file changed, 1 insertion(+), 1 deletion(-)
> +
> +diff --git a/Makefile b/Makefile
> +index 3583b93..78a2bf0 100644
> +--- a/Makefile
> ++++ b/Makefile
> +@@ -9,5 +9,5 @@ test: test.c
> +	$(CC) -O2 -march=native -mtune=native -Wall -Werror -pedantic test.c -o test
> +
> + test-debug: test.c
> +-	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test
> ++	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test-debug
> +
> +--
> +2.45.2
> diff --git a/patchwork/tests/series/dependency-one-cover.mbox.template b/patchwork/tests/series/dependency-one-cover.mbox.template
> new file mode 100644
> index 0000000..8817e69
> --- /dev/null
> +++ b/patchwork/tests/series/dependency-one-cover.mbox.template
> @@ -0,0 +1,128 @@
> +From ahassick@iol.unh.edu Tue Jun 11 16:08:46 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 0/2] Improvements to the test project
> +Date: Tue, 11 Jun 2024 12:08:46 -0400
> +Message-ID: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +Various improvements to the test project.
> +Guy Manson hasn't merged my other patches yet. That sack of lazy bones!
> +We will have to depend on the first series to get this to pass the CI.
> +
> +Depends-on: {depends_token}
> +Adam Hassick (2):
> +  Makefile: Improve the makefile
> +  test: Improve the test application
> +
> + Makefile |  8 ++++++--
> + test.c   | 22 +++++++++++++++++++---
> + 2 files changed, 25 insertions(+), 5 deletions(-)
> +
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Tue Jun 11 16:08:47 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 1/2] Makefile: Improve the makefile
> +Date: Tue, 11 Jun 2024 12:08:47 -0400
> +Message-ID: <20240611160854.192806-2-ahassick@iol.unh.edu>
> +In-Reply-To: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +References: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +We weren't checking for errors very well before. Let's fix that.
> +The test-debug target did not generate the correct file.
> +
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + Makefile | 8 ++++++--
> + 1 file changed, 6 insertions(+), 2 deletions(-)
> +
> +diff --git a/Makefile b/Makefile
> +index 2126a0e..3583b93 100644
> +--- a/Makefile
> ++++ b/Makefile
> +@@ -1,9 +1,13 @@
> +
> + CC=gcc
> +
> ++.PHONY: all
> ++
> ++all: test test-debug
> ++
> + test: test.c
> +-	$(CC) -O2 -march=native -mtune=native test.c -o test
> ++	$(CC) -O2 -march=native -mtune=native -Wall -Werror -pedantic test.c -o test
> +
> + test-debug: test.c
> +-	$(CC) -O0 -g test.c -o test
> ++	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test
> +
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Tue Jun 11 16:08:48 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 2/2] test: Improve the test application
> +Date: Tue, 11 Jun 2024 12:08:48 -0400
> +Message-ID: <20240611160854.192806-3-ahassick@iol.unh.edu>
> +In-Reply-To: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +References: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +The test application before wasn't very interesting.
> +Let's make it do something with an invariant this time.
> +
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + test.c | 22 +++++++++++++++++++---
> + 1 file changed, 19 insertions(+), 3 deletions(-)
> +
> +diff --git a/test.c b/test.c
> +index 5096204..f6cd16e 100644
> +--- a/test.c
> ++++ b/test.c
> +@@ -1,8 +1,24 @@
> +
> + #include <stdio.h>
> ++#include <string.h>
> +
> +-int main() {{
> +-	printf("HELLOOOOO!!!! Hi there!");
> +-	return 0;
> ++// This function does things.
> ++void thingymabob(char *string) {{
> ++	printf("Here's your argument: %s\n", string);
> ++	printf("Here is the length of your argument: %li\n", strlen(string));
> + }}
> +
> ++// Entry point.
> ++int main(int argc, char **argv) {{
> ++
> ++	// Make sure we get exactly one argument.
> ++	if (argc != 2) {{
> ++		fprintf(stderr, "I want exactly one argument please!\n");
> ++		return 1;
> ++	}}
> ++
> ++	// Do something with it.
> ++	thingymabob(argv[1]);
> ++
> ++	return 0;
> ++}}
> +--
> +2.45.2
> diff --git a/patchwork/tests/series/dependency-one-first-patch.mbox.template b/patchwork/tests/series/dependency-one-first-patch.mbox.template
> new file mode 100644
> index 0000000..d03ef7d
> --- /dev/null
> +++ b/patchwork/tests/series/dependency-one-first-patch.mbox.template
> @@ -0,0 +1,125 @@
> +From ahassick@iol.unh.edu Tue Jun 11 16:08:46 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 0/2] Improvements to the test project
> +Date: Tue, 11 Jun 2024 12:08:46 -0400
> +Message-ID: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +Various improvements to the test project.
> +
> +Depends-on: {depends_token}
> +Adam Hassick (2):
> +  Makefile: Improve the makefile
> +  test: Improve the test application
> +
> + Makefile |  8 ++++++--
> + test.c   | 22 +++++++++++++++++++---
> + 2 files changed, 25 insertions(+), 5 deletions(-)
> +
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Tue Jun 11 16:08:47 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 1/2] Makefile: Improve the makefile
> +Date: Tue, 11 Jun 2024 12:08:47 -0400
> +Message-ID: <20240611160854.192806-2-ahassick@iol.unh.edu>
> +In-Reply-To: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +References: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +We weren't checking for errors very well before. Let's fix that.
> +
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + Makefile | 8 ++++++--
> + 1 file changed, 6 insertions(+), 2 deletions(-)
> +
> +diff --git a/Makefile b/Makefile
> +index 2126a0e..3583b93 100644
> +--- a/Makefile
> ++++ b/Makefile
> +@@ -1,9 +1,13 @@
> +
> + CC=gcc
> +
> ++.PHONY: all
> ++
> ++all: test test-debug
> ++
> + test: test.c
> +-	$(CC) -O2 -march=native -mtune=native test.c -o test
> ++	$(CC) -O2 -march=native -mtune=native -Wall -Werror -pedantic test.c -o test
> +
> + test-debug: test.c
> +-	$(CC) -O0 -g test.c -o test
> ++	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test
> +
> +--
> +2.45.2
> +
> +
> +From ahassick@iol.unh.edu Tue Jun 11 16:08:48 2024
> +Return-Path: <ahassick@iol.unh.edu>
> +From: Adam Hassick <ahassick@iol.unh.edu>
> +To: Adam.Hassick@unh.edu
> +Cc: Adam Hassick <ahassick@iol.unh.edu>
> +Subject: [PATCH v1 2/2] test: Improve the test application
> +Date: Tue, 11 Jun 2024 12:08:48 -0400
> +Message-ID: <20240611160854.192806-3-ahassick@iol.unh.edu>
> +In-Reply-To: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +References: <20240611160854.192806-1-ahassick@iol.unh.edu>
> +MIME-Version: 1.0
> +Content-Transfer-Encoding: 8bit
> +
> +The test application before wasn't very interesting.
> +Let's make it do something with an invariant this time.
> +
> +Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
> +---
> + test.c | 22 +++++++++++++++++++---
> + 1 file changed, 19 insertions(+), 3 deletions(-)
> +
> +diff --git a/test.c b/test.c
> +index 5096204..f6cd16e 100644
> +--- a/test.c
> ++++ b/test.c
> +@@ -1,8 +1,24 @@
> +
> + #include <stdio.h>
> ++#include <string.h>
> +
> +-int main() {{
> +-	printf("HELLOOOOO!!!! Hi there!");
> +-	return 0;
> ++// This function does things.
> ++void thingymabob(char *string) {{
> ++	printf("Here's your argument: %s\n", string);
> ++	printf("Here is the length of your argument: %li\n", strlen(string));
> + }}
> +
> ++// Entry point.
> ++int main(int argc, char **argv) {{
> ++
> ++	// Make sure we get exactly one argument.
> ++	if (argc != 2) {{
> ++		fprintf(stderr, "I want exactly one argument please!\n");
> ++		return 1;
> ++	}}
> ++
> ++	// Do something with it.
> ++	thingymabob(argv[1]);
> ++
> ++	return 0;
> ++}}
> +--
> +2.45.2
> diff --git a/patchwork/tests/test_parser.py b/patchwork/tests/test_parser.py
> index 919f9f4..1411ef0 100644
> --- a/patchwork/tests/test_parser.py
> +++ b/patchwork/tests/test_parser.py
> @@ -1518,3 +1518,55 @@ class TestCommentCorrelation(TestCase):
>          result = parser.find_cover_for_comment(project, [msgid])
>  
>          self.assertEqual(cover, result)
> +
> +
> +class TestParseDependsOn(TestCase):
> +    def setUp(self):
> +        self.project = create_project()
> +        self.series1 = create_series(project=self.project)
> +        self.series2 = create_series(project=self.project)
> +        self.patch = create_patch(project=self.project, series=self.series1)
> +        self.cover = create_cover(project=self.project, series=self.series2)
> +
> +    def test_parse_depends_on_garbage_ref_1(self):
> +        self.assertFalse(
> +            parser.parse_depends_on('Depends-on: not-an-id-or-url')
> +        )
> +
> +    def test_parse_depends_on_garbage_ref_2(self):
> +        self.assertFalse(
> +            parser.parse_depends_on('Depends-on: <not-a-known-id@garbage.com>')
> +        )
> +
> +    def test_parse_depends_on_garbage_url(self):
> +        content = f'Depends-on: http://patchwork.dpdk.org/projects/{self.project.linkname}/list'
> +        self.assertFalse(parser.parse_depends_on(content))
> +
> +    def test_parse_depends_on_msgid(self):
> +        self.assertIn(
> +            self.series1,
> +            parser.parse_depends_on(f'Depends-on: {self.patch.msgid}'),
> +        )
> +        self.assertIn(
> +            self.series2,
> +            parser.parse_depends_on(f'Depends-on: {self.cover.msgid}'),
> +        )
> +
> +        content = (
> +            f'Depends-on: {self.patch.msgid}\nDepends-on: {self.cover.msgid}'
> +        )
> +        result = parser.parse_depends_on(content)
> +        self.assertIn(self.series1, result)
> +        self.assertIn(self.series2, result)
> +
> +    def test_parse_depends_on_url(self):
> +        content1 = f'Depends-on: http://test{self.series1.get_absolute_url()}'
> +        content2 = f'Depends-on: http://test{self.patch.get_absolute_url()}'
> +        content3 = f'Depends-on: http://test{self.series2.get_absolute_url()}'
> +        self.assertIn(self.series1, parser.parse_depends_on(content1))
> +        self.assertIn(self.series1, parser.parse_depends_on(content2))
> +        self.assertIn(self.series2, parser.parse_depends_on(content3))
> +
> +        result = parser.parse_depends_on('\n'.join([content2, content3]))
> +        self.assertIn(self.series1, result)
> +        self.assertIn(self.series2, result)
> diff --git a/patchwork/tests/test_series.py b/patchwork/tests/test_series.py
> index ce11404..8ae3f0a 100644
> --- a/patchwork/tests/test_series.py
> +++ b/patchwork/tests/test_series.py
> @@ -6,6 +6,7 @@
>  import mailbox
>  import os
>  import unittest
> +import tempfile
>  
>  from django.test import TestCase
>  
> @@ -804,3 +805,273 @@ class SeriesNameTestCase(TestCase):
>          self.assertEqual(series.name, series_name)
>  
>          mbox.close()
> +
> +
> +class SeriesDependencyTestCase(TestCase):
> +    def setUp(self):
> +        self.project = utils.create_project()
> +        utils.create_state()
> +
> +    def _load_mbox_template(self, name, **kwargs):
> +        """
> +        This function is necessary for this test so that we can template in
> +        the series or patch ID that we want to depend on.
> +        """
> +        with open(os.path.join(TEST_SERIES_DIR, name), 'r') as mbox_file:
> +            mbox_content = mbox_file.read()
> +
> +        # Write the templated mbox file to a temp file.
> +        tmpfile = tempfile.mktemp()
> +
> +        with open(tmpfile, 'w') as mbox_opt_file:
> +            mbox_opt_file.write(mbox_content.format(**kwargs))
> +
> +        # Load the mbox.
> +        mbox = mailbox.mbox(tmpfile, create=False)
> +
> +        # Then, remove the temp file (to avoid leaking resources).
> +        os.remove(tmpfile)
> +
> +        return mbox
> +
> +    def _load_mbox(self, name):
> +        return mailbox.mbox(os.path.join(TEST_SERIES_DIR, name), create=False)
> +
> +    def _parse_mbox(self, mbox, project_override=None):
> +        return list(
> +            map(
> +                lambda mail: parser.parse_mail(
> +                    mail,
> +                    project_override.listid
> +                    if project_override
> +                    else self.project.listid,
> +                ),
> +                mbox,
> +            )
> +        )
> +
> +    def test_dependency_by_series_url(self):
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        _, series1_patch1, _ = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        series1 = series1_patch1.series
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-cover.mbox.template',
> +            depends_token=f'http://test{series1.get_absolute_url()}',
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(mbox2)
> +        mbox2.close()
> +
> +        series2 = series2_patch1.series
> +
> +        self.assertIn(series2, series1.dependents.all())
> +        self.assertIn(series1, series2.dependencies.all())
> +
> +    def test_dependency_by_patch_url(self):
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        _, series1_patch1, _ = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        series1 = series1_patch1.series
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-cover.mbox.template',
> +            depends_token=f'http://test{series1_patch1.get_absolute_url()}',
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(mbox2)
> +        mbox2.close()
> +
> +        series2 = series2_patch1.series
> +
> +        self.assertIn(series2, series1.dependents.all())
> +        self.assertIn(series1, series2.dependencies.all())
> +
> +    def test_dependency_by_patch_msgid(self):
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        _, series1_patch1, _ = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        series1 = series1_patch1.series
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-first-patch.mbox.template',
> +            depends_token=series1_patch1.msgid,
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(mbox2)
> +        mbox2.close()
> +
> +        series2 = series2_patch1.series
> +
> +        self.assertIn(series2, series1.dependents.all())
> +        self.assertIn(series1, series2.dependencies.all())
> +
> +    def test_dependency_by_cover_msgid(self):
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        series1_cover, series1_patch1, _ = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-first-patch.mbox.template',
> +            depends_token=series1_cover.msgid,
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(mbox2)
> +        mbox2.close()
> +
> +        series1 = series1_patch1.series
> +        series2 = series2_patch1.series
> +
> +        self.assertIn(series2, series1.dependents.all())
> +        self.assertIn(series1, series2.dependencies.all())
> +
> +    def test_dependency_by_patch_msgid_on_cover(self):
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        _, series1_patch1, _ = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        series1 = series1_patch1.series
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-cover.mbox.template',
> +            depends_token=series1_patch1.msgid,
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(mbox2)
> +        mbox2.close()
> +
> +        series2 = series2_patch1.series
> +
> +        self.assertIn(series2, series1.dependents.all())
> +        self.assertIn(series1, series2.dependencies.all())
> +
> +    def test_dependency_by_patch2_msgid(self):
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        _, _, series1_patch2 = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-cover.mbox.template',
> +            depends_token=series1_patch2.msgid,
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(mbox2)
> +        mbox2.close()
> +
> +        series1 = series1_patch2.series
> +        series2 = series2_patch1.series
> +
> +        self.assertIn(series2, series1.dependents.all())
> +        self.assertIn(series1, series2.dependencies.all())
> +
> +    def test_dependency_no_circular_relation(self):
> +        mbox = self._load_mbox_template(
> +            'dependency-one-first-patch.mbox.template',
> +            # Message ID from the cover letter in this patch,
> +            #   which should be parsed ahead of the 1st patch.
> +            depends_token='<20240611160854.192806-1-ahassick@iol.unh.edu>',
> +        )
> +        _, patch, _ = self._parse_mbox(mbox)
> +        mbox.close()
> +
> +        self.assertNotIn(patch.series, patch.series.dependencies.all())
> +
> +    def test_dependency_no_cross_project_relation(self):
> +        project2 = utils.create_project(name='testproject2')
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        _, series1_patch1, _ = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        series1 = series1_patch1.series
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-first-patch.mbox.template',
> +            depends_token=series1_patch1.msgid,
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(
> +            mbox2, project_override=project2
> +        )
> +        mbox2.close()
> +
> +        series2 = series2_patch1.series
> +
> +        self.assertNotIn(series1, series2.dependencies.all())
> +
> +    def test_dependency_multi_1(self):
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        _, series1_patch1, _ = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        series1 = series1_patch1.series
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-cover.mbox.template',
> +            depends_token=series1_patch1.msgid,
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(mbox2)
> +        mbox2.close()
> +
> +        series2 = series2_patch1.series
> +
> +        mbox3 = self._load_mbox_template(
> +            'dependency-multi.mbox.template',
> +            depends_token_1=series1_patch1.msgid,
> +            depends_token_2=series2_patch1.msgid,
> +        )
> +        _, series3_patch1, _ = self._parse_mbox(mbox3)
> +        mbox3.close()
> +
> +        series3 = series3_patch1.series
> +
> +        self.assertIn(series2, series1.dependents.all())
> +        self.assertIn(series2, series3.dependencies.all())
> +        self.assertIn(series1, series2.dependencies.all())
> +        self.assertIn(series3, series2.dependents.all())
> +        self.assertEqual(series1.dependencies.count(), 0)
> +        self.assertEqual(series1.dependents.count(), 2)
> +        self.assertEqual(series2.dependents.count(), 1)
> +        self.assertEqual(series2.dependencies.count(), 1)
> +        self.assertEqual(series3.dependencies.count(), 2)
> +        self.assertEqual(series3.dependents.count(), 0)
> +
> +        # Test that the ordering is correct.
> +        d1, d2 = [dep for dep in series3.dependencies.all()]
> +
> +        # First item in the query set should be the first dependency.
> +        self.assertEqual(d1, series1)
> +        self.assertEqual(d2, series2)
> +
> +    def test_dependency_multi_2(self):
> +        mbox1 = self._load_mbox('dependency-base-patch.mbox')
> +        _, series1_patch1, _ = self._parse_mbox(mbox1)
> +        mbox1.close()
> +
> +        series1 = series1_patch1.series
> +
> +        mbox2 = self._load_mbox_template(
> +            'dependency-one-cover.mbox.template',
> +            depends_token=series1_patch1.msgid,
> +        )
> +        _, series2_patch1, _ = self._parse_mbox(mbox2)
> +        mbox2.close()
> +
> +        series2 = series2_patch1.series
> +
> +        mbox3 = self._load_mbox_template(
> +            'dependency-multi-2.mbox.template',
> +            depends_token_1=series1_patch1.msgid,
> +            depends_token_2=series2_patch1.msgid,
> +        )
> +        _, series3_patch1, _ = self._parse_mbox(mbox3)
> +        mbox3.close()
> +
> +        series3 = series3_patch1.series
> +
> +        self.assertIn(series2, series1.dependents.all())
> +        self.assertIn(series2, series3.dependencies.all())
> +        self.assertIn(series1, series2.dependencies.all())
> +        self.assertIn(series3, series2.dependents.all())
> +        self.assertEqual(series1.dependencies.count(), 0)
> +        self.assertEqual(series1.dependents.count(), 2)
> +        self.assertEqual(series2.dependents.count(), 1)
> +        self.assertEqual(series2.dependencies.count(), 1)
> +        self.assertEqual(series3.dependencies.count(), 2)
> +        self.assertEqual(series3.dependents.count(), 0)
diff mbox series

Patch

diff --git a/patchwork/tests/api/test_series.py b/patchwork/tests/api/test_series.py
index 730678a..86954b0 100644
--- a/patchwork/tests/api/test_series.py
+++ b/patchwork/tests/api/test_series.py
@@ -44,6 +44,20 @@  class TestSeriesAPI(utils.APITestCase):
         self.assertIn(series_obj.get_mbox_url(), series_json['mbox'])
         self.assertIn(series_obj.get_absolute_url(), series_json['web_url'])
 
+        for dep, item in zip(
+            series_obj.dependencies.all(), series_json['dependencies']
+        ):
+            self.assertIn(
+                reverse('api-series-detail', kwargs={'pk': dep.id}), item
+            )
+
+        for dep, item in zip(
+            series_obj.dependents.all(), series_json['dependents']
+        ):
+            self.assertIn(
+                reverse('api-series-detail', kwargs={'pk': dep.id}), item
+            )
+
         # nested fields
 
         self.assertEqual(series_obj.project.id, series_json['project']['id'])
@@ -95,6 +109,21 @@  class TestSeriesAPI(utils.APITestCase):
         series_rsp = resp.data[0]
         self.assertSerialized(series, series_rsp)
 
+    def test_dependencies(self):
+        project_obj = create_project(linkname='myproject')
+        person_obj = create_person(email='test@example.com')
+        series1 = create_series(project=project_obj, submitter=person_obj)
+        create_cover(series=series1)
+        create_patch(series=series1)
+        series2 = create_series(project=project_obj, submitter=person_obj)
+        create_cover(series=series2)
+        create_patch(series=series2)
+        series1.add_dependencies([series2])
+        resp = self.client.get(self.api_url())
+        self.assertEqual(2, len(resp.data))
+        self.assertSerialized(series2, resp.data[1])
+        self.assertSerialized(series1, resp.data[0])
+
     def test_list_filter_project(self):
         """Filter series by project."""
         series = self._create_series()
@@ -152,7 +181,7 @@  class TestSeriesAPI(utils.APITestCase):
             create_cover(series=series_obj)
             create_patch(series=series_obj)
 
-        with self.assertNumQueries(6):
+        with self.assertNumQueries(8):
             self.client.get(self.api_url())
 
     @utils.store_samples('series-detail')
@@ -175,6 +204,8 @@  class TestSeriesAPI(utils.APITestCase):
         self.assertNotIn('web_url', resp.data['cover_letter'])
         self.assertNotIn('mbox', resp.data['cover_letter'])
         self.assertNotIn('web_url', resp.data['patches'][0])
+        self.assertNotIn('dependents', resp.data)
+        self.assertNotIn('dependencies', resp.data)
 
     def test_detail_non_existent(self):
         """Ensure we get a 404 for a non-existent series."""
diff --git a/patchwork/tests/series/dependency-base-patch.mbox b/patchwork/tests/series/dependency-base-patch.mbox
new file mode 100644
index 0000000..e7f9e94
--- /dev/null
+++ b/patchwork/tests/series/dependency-base-patch.mbox
@@ -0,0 +1,102 @@ 
+From ahassick@iol.unh.edu Mon Jun 10 21:09:07 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Subject: [PATCH v1 0/2] Add test files for testing
+Date: Mon, 10 Jun 2024 17:09:07 -0400
+Message-ID: <20240610210912.161735-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+Add a test program and a makefile.
+Big surprise here, but this is all for testing.
+
+Adam Hassick (2):
+  Add test program
+  Add a Makefile
+
+ Makefile | 9 +++++++++
+ test.c   | 8 ++++++++
+ 2 files changed, 17 insertions(+)
+ create mode 100644 Makefile
+ create mode 100644 test.c
+
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Mon Jun 10 21:09:09 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Subject: [PATCH v1 1/2] Add test program
+Date: Mon, 10 Jun 2024 17:09:09 -0400
+Message-ID: <20240610210912.161735-3-ahassick@iol.unh.edu>
+In-Reply-To: <20240610210912.161735-1-ahassick@iol.unh.edu>
+References: <20240610210912.161735-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+
+
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ test.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+ create mode 100644 test.c
+
+diff --git a/test.c b/test.c
+new file mode 100644
+index 0000000..5096204
+--- /dev/null
++++ b/test.c
+@@ -0,0 +1,8 @@
++
++#include <stdio.h>
++
++int main() {
++	printf("HELLOOOOO!!!! Hi there!");
++	return 0;
++}
++
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Mon Jun 10 21:09:11 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Subject: [PATCH v1 2/2] Add a Makefile
+Date: Mon, 10 Jun 2024 17:09:11 -0400
+Message-ID: <20240610210912.161735-5-ahassick@iol.unh.edu>
+In-Reply-To: <20240610210912.161735-1-ahassick@iol.unh.edu>
+References: <20240610210912.161735-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+How are people supposed to build this without a Makefile?
+
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ Makefile | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+ create mode 100644 Makefile
+
+diff --git a/Makefile b/Makefile
+new file mode 100644
+index 0000000..2126a0e
+--- /dev/null
++++ b/Makefile
+@@ -0,0 +1,9 @@
++
++CC=gcc
++
++test: test.c
++	$(CC) -O2 -march=native -mtune=native test.c -o test
++
++test-debug: test.c
++	$(CC) -O0 -g test.c -o test
++
+--
+2.45.2
diff --git a/patchwork/tests/series/dependency-multi-2.mbox.template b/patchwork/tests/series/dependency-multi-2.mbox.template
new file mode 100644
index 0000000..42296f4
--- /dev/null
+++ b/patchwork/tests/series/dependency-multi-2.mbox.template
@@ -0,0 +1,110 @@ 
+From ahassick@iol.unh.edu Wed Jun 12 20:40:05 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 0/2] More changes
+Date: Wed, 12 Jun 2024 16:40:05 -0400
+Message-ID: <20240612204019.364820-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+Tests.
+
+Adam Hassick (2):
+  test: Change the test function
+  Fix Makefile
+
+ Makefile |  2 +-
+ test.c   | 10 ++++++++--
+ 2 files changed, 9 insertions(+), 3 deletions(-)
+
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Wed Jun 12 20:40:06 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 1/2] test: Change the test function
+Date: Wed, 12 Jun 2024 16:40:06 -0400
+Message-ID: <20240612204019.364820-2-ahassick@iol.unh.edu>
+In-Reply-To: <20240612204019.364820-1-ahassick@iol.unh.edu>
+References: <20240612204019.364820-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+I don't like what it did before. Let's make it do something more
+interesting this time.
+
+Depends-on: {depends_token_1}
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ test.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/test.c b/test.c
+index f6cd16e..a868311 100644
+--- a/test.c
++++ b/test.c
+@@ -1,11 +1,17 @@
+
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <string.h>
+
+ // This function does things.
+ void thingymabob(char *string) {{
+-	printf("Here's your argument: %s\n", string);
+-	printf("Here is the length of your argument: %li\n", strlen(string));
++	double f = atof(string);
++
++	if (f > 5.0) {{
++		printf("I'm satisfied.\n");
++	}} else {{
++		fprintf(stderr, "I'm not satisfied.\n");
++	}}
+ }}
+
+ // Entry point.
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Wed Jun 12 20:40:07 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 2/2] Fix Makefile
+Date: Wed, 12 Jun 2024 16:40:07 -0400
+Message-ID: <20240612204019.364820-3-ahassick@iol.unh.edu>
+X-Mailer: git-send-email 2.45.2
+In-Reply-To: <20240612204019.364820-1-ahassick@iol.unh.edu>
+References: <20240612204019.364820-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+The test-debug target did not generate the correct file.
+
+Depends-on: {depends_token_2}
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile b/Makefile
+index 3583b93..78a2bf0 100644
+--- a/Makefile
++++ b/Makefile
+@@ -9,5 +9,5 @@ test: test.c
+	$(CC) -O2 -march=native -mtune=native -Wall -Werror -pedantic test.c -o test
+
+ test-debug: test.c
+-	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test
++	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test-debug
+
+--
+2.45.2
diff --git a/patchwork/tests/series/dependency-multi.mbox.template b/patchwork/tests/series/dependency-multi.mbox.template
new file mode 100644
index 0000000..40fba59
--- /dev/null
+++ b/patchwork/tests/series/dependency-multi.mbox.template
@@ -0,0 +1,109 @@ 
+From ahassick@iol.unh.edu Wed Jun 12 20:40:05 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: ach1062@usnh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 0/2] More changes
+Date: Wed, 12 Jun 2024 16:40:05 -0400
+Message-ID: <20240612204019.364820-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+Tests.
+
+Depends-on: {depends_token_1}
+Depends-on: {depends_token_2}
+Adam Hassick (2):
+  test: Change the test function
+  Fix Makefile
+
+ Makefile |  2 +-
+ test.c   | 10 ++++++++--
+ 2 files changed, 9 insertions(+), 3 deletions(-)
+
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Wed Jun 12 20:40:06 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: ach1062@usnh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 1/2] test: Change the test function
+Date: Wed, 12 Jun 2024 16:40:06 -0400
+Message-ID: <20240612204019.364820-2-ahassick@iol.unh.edu>
+In-Reply-To: <20240612204019.364820-1-ahassick@iol.unh.edu>
+References: <20240612204019.364820-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+I don't like what it did before. Let's make it do something more
+interesting this time.
+
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ test.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/test.c b/test.c
+index f6cd16e..a868311 100644
+--- a/test.c
++++ b/test.c
+@@ -1,11 +1,17 @@
+
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <string.h>
+
+ // This function does things.
+ void thingymabob(char *string) {{
+-	printf("Here's your argument: %s\n", string);
+-	printf("Here is the length of your argument: %li\n", strlen(string));
++	double f = atof(string);
++
++	if (f > 5.0) {{
++		printf("I'm satisfied.\n");
++	}} else {{
++		fprintf(stderr, "I'm not satisfied.\n");
++	}}
+ }}
+
+ // Entry point.
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Wed Jun 12 20:40:07 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: ach1062@usnh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 2/2] Fix Makefile
+Date: Wed, 12 Jun 2024 16:40:07 -0400
+Message-ID: <20240612204019.364820-3-ahassick@iol.unh.edu>
+In-Reply-To: <20240612204019.364820-1-ahassick@iol.unh.edu>
+References: <20240612204019.364820-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+The test-debug target did not generate the correct file.
+
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile b/Makefile
+index 3583b93..78a2bf0 100644
+--- a/Makefile
++++ b/Makefile
+@@ -9,5 +9,5 @@ test: test.c
+	$(CC) -O2 -march=native -mtune=native -Wall -Werror -pedantic test.c -o test
+
+ test-debug: test.c
+-	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test
++	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test-debug
+
+--
+2.45.2
diff --git a/patchwork/tests/series/dependency-one-cover.mbox.template b/patchwork/tests/series/dependency-one-cover.mbox.template
new file mode 100644
index 0000000..8817e69
--- /dev/null
+++ b/patchwork/tests/series/dependency-one-cover.mbox.template
@@ -0,0 +1,128 @@ 
+From ahassick@iol.unh.edu Tue Jun 11 16:08:46 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 0/2] Improvements to the test project
+Date: Tue, 11 Jun 2024 12:08:46 -0400
+Message-ID: <20240611160854.192806-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+Various improvements to the test project.
+Guy Manson hasn't merged my other patches yet. That sack of lazy bones!
+We will have to depend on the first series to get this to pass the CI.
+
+Depends-on: {depends_token}
+Adam Hassick (2):
+  Makefile: Improve the makefile
+  test: Improve the test application
+
+ Makefile |  8 ++++++--
+ test.c   | 22 +++++++++++++++++++---
+ 2 files changed, 25 insertions(+), 5 deletions(-)
+
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Tue Jun 11 16:08:47 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 1/2] Makefile: Improve the makefile
+Date: Tue, 11 Jun 2024 12:08:47 -0400
+Message-ID: <20240611160854.192806-2-ahassick@iol.unh.edu>
+In-Reply-To: <20240611160854.192806-1-ahassick@iol.unh.edu>
+References: <20240611160854.192806-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+We weren't checking for errors very well before. Let's fix that.
+The test-debug target did not generate the correct file.
+
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ Makefile | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 2126a0e..3583b93 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,9 +1,13 @@
+
+ CC=gcc
+
++.PHONY: all
++
++all: test test-debug
++
+ test: test.c
+-	$(CC) -O2 -march=native -mtune=native test.c -o test
++	$(CC) -O2 -march=native -mtune=native -Wall -Werror -pedantic test.c -o test
+
+ test-debug: test.c
+-	$(CC) -O0 -g test.c -o test
++	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test
+
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Tue Jun 11 16:08:48 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 2/2] test: Improve the test application
+Date: Tue, 11 Jun 2024 12:08:48 -0400
+Message-ID: <20240611160854.192806-3-ahassick@iol.unh.edu>
+In-Reply-To: <20240611160854.192806-1-ahassick@iol.unh.edu>
+References: <20240611160854.192806-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+The test application before wasn't very interesting.
+Let's make it do something with an invariant this time.
+
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ test.c | 22 +++++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/test.c b/test.c
+index 5096204..f6cd16e 100644
+--- a/test.c
++++ b/test.c
+@@ -1,8 +1,24 @@
+
+ #include <stdio.h>
++#include <string.h>
+
+-int main() {{
+-	printf("HELLOOOOO!!!! Hi there!");
+-	return 0;
++// This function does things.
++void thingymabob(char *string) {{
++	printf("Here's your argument: %s\n", string);
++	printf("Here is the length of your argument: %li\n", strlen(string));
+ }}
+
++// Entry point.
++int main(int argc, char **argv) {{
++
++	// Make sure we get exactly one argument.
++	if (argc != 2) {{
++		fprintf(stderr, "I want exactly one argument please!\n");
++		return 1;
++	}}
++
++	// Do something with it.
++	thingymabob(argv[1]);
++
++	return 0;
++}}
+--
+2.45.2
diff --git a/patchwork/tests/series/dependency-one-first-patch.mbox.template b/patchwork/tests/series/dependency-one-first-patch.mbox.template
new file mode 100644
index 0000000..d03ef7d
--- /dev/null
+++ b/patchwork/tests/series/dependency-one-first-patch.mbox.template
@@ -0,0 +1,125 @@ 
+From ahassick@iol.unh.edu Tue Jun 11 16:08:46 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 0/2] Improvements to the test project
+Date: Tue, 11 Jun 2024 12:08:46 -0400
+Message-ID: <20240611160854.192806-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+Various improvements to the test project.
+
+Depends-on: {depends_token}
+Adam Hassick (2):
+  Makefile: Improve the makefile
+  test: Improve the test application
+
+ Makefile |  8 ++++++--
+ test.c   | 22 +++++++++++++++++++---
+ 2 files changed, 25 insertions(+), 5 deletions(-)
+
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Tue Jun 11 16:08:47 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 1/2] Makefile: Improve the makefile
+Date: Tue, 11 Jun 2024 12:08:47 -0400
+Message-ID: <20240611160854.192806-2-ahassick@iol.unh.edu>
+In-Reply-To: <20240611160854.192806-1-ahassick@iol.unh.edu>
+References: <20240611160854.192806-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+We weren't checking for errors very well before. Let's fix that.
+
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ Makefile | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 2126a0e..3583b93 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,9 +1,13 @@
+
+ CC=gcc
+
++.PHONY: all
++
++all: test test-debug
++
+ test: test.c
+-	$(CC) -O2 -march=native -mtune=native test.c -o test
++	$(CC) -O2 -march=native -mtune=native -Wall -Werror -pedantic test.c -o test
+
+ test-debug: test.c
+-	$(CC) -O0 -g test.c -o test
++	$(CC) -O0 -g test.c -Wall -Werror -pedantic -o test
+
+--
+2.45.2
+
+
+From ahassick@iol.unh.edu Tue Jun 11 16:08:48 2024
+Return-Path: <ahassick@iol.unh.edu>
+From: Adam Hassick <ahassick@iol.unh.edu>
+To: Adam.Hassick@unh.edu
+Cc: Adam Hassick <ahassick@iol.unh.edu>
+Subject: [PATCH v1 2/2] test: Improve the test application
+Date: Tue, 11 Jun 2024 12:08:48 -0400
+Message-ID: <20240611160854.192806-3-ahassick@iol.unh.edu>
+In-Reply-To: <20240611160854.192806-1-ahassick@iol.unh.edu>
+References: <20240611160854.192806-1-ahassick@iol.unh.edu>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+
+The test application before wasn't very interesting.
+Let's make it do something with an invariant this time.
+
+Signed-off-by: Adam Hassick <ahassick@iol.unh.edu>
+---
+ test.c | 22 +++++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/test.c b/test.c
+index 5096204..f6cd16e 100644
+--- a/test.c
++++ b/test.c
+@@ -1,8 +1,24 @@
+
+ #include <stdio.h>
++#include <string.h>
+
+-int main() {{
+-	printf("HELLOOOOO!!!! Hi there!");
+-	return 0;
++// This function does things.
++void thingymabob(char *string) {{
++	printf("Here's your argument: %s\n", string);
++	printf("Here is the length of your argument: %li\n", strlen(string));
+ }}
+
++// Entry point.
++int main(int argc, char **argv) {{
++
++	// Make sure we get exactly one argument.
++	if (argc != 2) {{
++		fprintf(stderr, "I want exactly one argument please!\n");
++		return 1;
++	}}
++
++	// Do something with it.
++	thingymabob(argv[1]);
++
++	return 0;
++}}
+--
+2.45.2
diff --git a/patchwork/tests/test_parser.py b/patchwork/tests/test_parser.py
index 919f9f4..1411ef0 100644
--- a/patchwork/tests/test_parser.py
+++ b/patchwork/tests/test_parser.py
@@ -1518,3 +1518,55 @@  class TestCommentCorrelation(TestCase):
         result = parser.find_cover_for_comment(project, [msgid])
 
         self.assertEqual(cover, result)
+
+
+class TestParseDependsOn(TestCase):
+    def setUp(self):
+        self.project = create_project()
+        self.series1 = create_series(project=self.project)
+        self.series2 = create_series(project=self.project)
+        self.patch = create_patch(project=self.project, series=self.series1)
+        self.cover = create_cover(project=self.project, series=self.series2)
+
+    def test_parse_depends_on_garbage_ref_1(self):
+        self.assertFalse(
+            parser.parse_depends_on('Depends-on: not-an-id-or-url')
+        )
+
+    def test_parse_depends_on_garbage_ref_2(self):
+        self.assertFalse(
+            parser.parse_depends_on('Depends-on: <not-a-known-id@garbage.com>')
+        )
+
+    def test_parse_depends_on_garbage_url(self):
+        content = f'Depends-on: http://patchwork.dpdk.org/projects/{self.project.linkname}/list'
+        self.assertFalse(parser.parse_depends_on(content))
+
+    def test_parse_depends_on_msgid(self):
+        self.assertIn(
+            self.series1,
+            parser.parse_depends_on(f'Depends-on: {self.patch.msgid}'),
+        )
+        self.assertIn(
+            self.series2,
+            parser.parse_depends_on(f'Depends-on: {self.cover.msgid}'),
+        )
+
+        content = (
+            f'Depends-on: {self.patch.msgid}\nDepends-on: {self.cover.msgid}'
+        )
+        result = parser.parse_depends_on(content)
+        self.assertIn(self.series1, result)
+        self.assertIn(self.series2, result)
+
+    def test_parse_depends_on_url(self):
+        content1 = f'Depends-on: http://test{self.series1.get_absolute_url()}'
+        content2 = f'Depends-on: http://test{self.patch.get_absolute_url()}'
+        content3 = f'Depends-on: http://test{self.series2.get_absolute_url()}'
+        self.assertIn(self.series1, parser.parse_depends_on(content1))
+        self.assertIn(self.series1, parser.parse_depends_on(content2))
+        self.assertIn(self.series2, parser.parse_depends_on(content3))
+
+        result = parser.parse_depends_on('\n'.join([content2, content3]))
+        self.assertIn(self.series1, result)
+        self.assertIn(self.series2, result)
diff --git a/patchwork/tests/test_series.py b/patchwork/tests/test_series.py
index ce11404..8ae3f0a 100644
--- a/patchwork/tests/test_series.py
+++ b/patchwork/tests/test_series.py
@@ -6,6 +6,7 @@ 
 import mailbox
 import os
 import unittest
+import tempfile
 
 from django.test import TestCase
 
@@ -804,3 +805,273 @@  class SeriesNameTestCase(TestCase):
         self.assertEqual(series.name, series_name)
 
         mbox.close()
+
+
+class SeriesDependencyTestCase(TestCase):
+    def setUp(self):
+        self.project = utils.create_project()
+        utils.create_state()
+
+    def _load_mbox_template(self, name, **kwargs):
+        """
+        This function is necessary for this test so that we can template in
+        the series or patch ID that we want to depend on.
+        """
+        with open(os.path.join(TEST_SERIES_DIR, name), 'r') as mbox_file:
+            mbox_content = mbox_file.read()
+
+        # Write the templated mbox file to a temp file.
+        tmpfile = tempfile.mktemp()
+
+        with open(tmpfile, 'w') as mbox_opt_file:
+            mbox_opt_file.write(mbox_content.format(**kwargs))
+
+        # Load the mbox.
+        mbox = mailbox.mbox(tmpfile, create=False)
+
+        # Then, remove the temp file (to avoid leaking resources).
+        os.remove(tmpfile)
+
+        return mbox
+
+    def _load_mbox(self, name):
+        return mailbox.mbox(os.path.join(TEST_SERIES_DIR, name), create=False)
+
+    def _parse_mbox(self, mbox, project_override=None):
+        return list(
+            map(
+                lambda mail: parser.parse_mail(
+                    mail,
+                    project_override.listid
+                    if project_override
+                    else self.project.listid,
+                ),
+                mbox,
+            )
+        )
+
+    def test_dependency_by_series_url(self):
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        _, series1_patch1, _ = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        series1 = series1_patch1.series
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-cover.mbox.template',
+            depends_token=f'http://test{series1.get_absolute_url()}',
+        )
+        _, series2_patch1, _ = self._parse_mbox(mbox2)
+        mbox2.close()
+
+        series2 = series2_patch1.series
+
+        self.assertIn(series2, series1.dependents.all())
+        self.assertIn(series1, series2.dependencies.all())
+
+    def test_dependency_by_patch_url(self):
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        _, series1_patch1, _ = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        series1 = series1_patch1.series
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-cover.mbox.template',
+            depends_token=f'http://test{series1_patch1.get_absolute_url()}',
+        )
+        _, series2_patch1, _ = self._parse_mbox(mbox2)
+        mbox2.close()
+
+        series2 = series2_patch1.series
+
+        self.assertIn(series2, series1.dependents.all())
+        self.assertIn(series1, series2.dependencies.all())
+
+    def test_dependency_by_patch_msgid(self):
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        _, series1_patch1, _ = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        series1 = series1_patch1.series
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-first-patch.mbox.template',
+            depends_token=series1_patch1.msgid,
+        )
+        _, series2_patch1, _ = self._parse_mbox(mbox2)
+        mbox2.close()
+
+        series2 = series2_patch1.series
+
+        self.assertIn(series2, series1.dependents.all())
+        self.assertIn(series1, series2.dependencies.all())
+
+    def test_dependency_by_cover_msgid(self):
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        series1_cover, series1_patch1, _ = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-first-patch.mbox.template',
+            depends_token=series1_cover.msgid,
+        )
+        _, series2_patch1, _ = self._parse_mbox(mbox2)
+        mbox2.close()
+
+        series1 = series1_patch1.series
+        series2 = series2_patch1.series
+
+        self.assertIn(series2, series1.dependents.all())
+        self.assertIn(series1, series2.dependencies.all())
+
+    def test_dependency_by_patch_msgid_on_cover(self):
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        _, series1_patch1, _ = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        series1 = series1_patch1.series
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-cover.mbox.template',
+            depends_token=series1_patch1.msgid,
+        )
+        _, series2_patch1, _ = self._parse_mbox(mbox2)
+        mbox2.close()
+
+        series2 = series2_patch1.series
+
+        self.assertIn(series2, series1.dependents.all())
+        self.assertIn(series1, series2.dependencies.all())
+
+    def test_dependency_by_patch2_msgid(self):
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        _, _, series1_patch2 = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-cover.mbox.template',
+            depends_token=series1_patch2.msgid,
+        )
+        _, series2_patch1, _ = self._parse_mbox(mbox2)
+        mbox2.close()
+
+        series1 = series1_patch2.series
+        series2 = series2_patch1.series
+
+        self.assertIn(series2, series1.dependents.all())
+        self.assertIn(series1, series2.dependencies.all())
+
+    def test_dependency_no_circular_relation(self):
+        mbox = self._load_mbox_template(
+            'dependency-one-first-patch.mbox.template',
+            # Message ID from the cover letter in this patch,
+            #   which should be parsed ahead of the 1st patch.
+            depends_token='<20240611160854.192806-1-ahassick@iol.unh.edu>',
+        )
+        _, patch, _ = self._parse_mbox(mbox)
+        mbox.close()
+
+        self.assertNotIn(patch.series, patch.series.dependencies.all())
+
+    def test_dependency_no_cross_project_relation(self):
+        project2 = utils.create_project(name='testproject2')
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        _, series1_patch1, _ = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        series1 = series1_patch1.series
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-first-patch.mbox.template',
+            depends_token=series1_patch1.msgid,
+        )
+        _, series2_patch1, _ = self._parse_mbox(
+            mbox2, project_override=project2
+        )
+        mbox2.close()
+
+        series2 = series2_patch1.series
+
+        self.assertNotIn(series1, series2.dependencies.all())
+
+    def test_dependency_multi_1(self):
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        _, series1_patch1, _ = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        series1 = series1_patch1.series
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-cover.mbox.template',
+            depends_token=series1_patch1.msgid,
+        )
+        _, series2_patch1, _ = self._parse_mbox(mbox2)
+        mbox2.close()
+
+        series2 = series2_patch1.series
+
+        mbox3 = self._load_mbox_template(
+            'dependency-multi.mbox.template',
+            depends_token_1=series1_patch1.msgid,
+            depends_token_2=series2_patch1.msgid,
+        )
+        _, series3_patch1, _ = self._parse_mbox(mbox3)
+        mbox3.close()
+
+        series3 = series3_patch1.series
+
+        self.assertIn(series2, series1.dependents.all())
+        self.assertIn(series2, series3.dependencies.all())
+        self.assertIn(series1, series2.dependencies.all())
+        self.assertIn(series3, series2.dependents.all())
+        self.assertEqual(series1.dependencies.count(), 0)
+        self.assertEqual(series1.dependents.count(), 2)
+        self.assertEqual(series2.dependents.count(), 1)
+        self.assertEqual(series2.dependencies.count(), 1)
+        self.assertEqual(series3.dependencies.count(), 2)
+        self.assertEqual(series3.dependents.count(), 0)
+
+        # Test that the ordering is correct.
+        d1, d2 = [dep for dep in series3.dependencies.all()]
+
+        # First item in the query set should be the first dependency.
+        self.assertEqual(d1, series1)
+        self.assertEqual(d2, series2)
+
+    def test_dependency_multi_2(self):
+        mbox1 = self._load_mbox('dependency-base-patch.mbox')
+        _, series1_patch1, _ = self._parse_mbox(mbox1)
+        mbox1.close()
+
+        series1 = series1_patch1.series
+
+        mbox2 = self._load_mbox_template(
+            'dependency-one-cover.mbox.template',
+            depends_token=series1_patch1.msgid,
+        )
+        _, series2_patch1, _ = self._parse_mbox(mbox2)
+        mbox2.close()
+
+        series2 = series2_patch1.series
+
+        mbox3 = self._load_mbox_template(
+            'dependency-multi-2.mbox.template',
+            depends_token_1=series1_patch1.msgid,
+            depends_token_2=series2_patch1.msgid,
+        )
+        _, series3_patch1, _ = self._parse_mbox(mbox3)
+        mbox3.close()
+
+        series3 = series3_patch1.series
+
+        self.assertIn(series2, series1.dependents.all())
+        self.assertIn(series2, series3.dependencies.all())
+        self.assertIn(series1, series2.dependencies.all())
+        self.assertIn(series3, series2.dependents.all())
+        self.assertEqual(series1.dependencies.count(), 0)
+        self.assertEqual(series1.dependents.count(), 2)
+        self.assertEqual(series2.dependents.count(), 1)
+        self.assertEqual(series2.dependencies.count(), 1)
+        self.assertEqual(series3.dependencies.count(), 2)
+        self.assertEqual(series3.dependents.count(), 0)