Skip to content

Commit

Permalink
Fix #2696: Segfault with bad dub.sdl
Browse files Browse the repository at this point in the history
This was a null pointer being passed down.
However, while looking at the code, it was obvious that similar
bugs would trigger for subpackages (as the code was not handling
`null` packages either), so test cases for those have been added.
  • Loading branch information
Geod24 committed Dec 28, 2023
1 parent 518b585 commit a6cc7a6
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 1 deletion.
7 changes: 6 additions & 1 deletion source/dub/project.d
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,9 @@ class Project {
m_missingDependencies = [];

Package resolveSubPackage(Package p, string subname, bool silentFail) {
return subname.length ? m_packageManager.getSubPackage(p, subname, silentFail) : p;
if (!subname.length || p is null)
return p;
return m_packageManager.getSubPackage(p, subname, silentFail);
}

void collectDependenciesRec(Package pack, int depth = 0)
Expand Down Expand Up @@ -556,6 +558,9 @@ class Project {
if (!vspec.repository.empty) {
p = m_packageManager.loadSCMPackage(basename, vspec.repository);
resolveSubPackage(p, subname, false);
enforce(p !is null,
"Unable to fetch '%s@%s' using git - does the repository and version exists?".format(
dep.name, vspec.repository));
} else if (!vspec.path.empty && is_desired) {
NativePath path = vspec.path;
if (!path.absolute) path = pack.path ~ path;
Expand Down
46 changes: 46 additions & 0 deletions source/dub/test/base.d
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ public class TestSelectedVersions : SelectedVersions {
*/
package class TestPackageManager : PackageManager
{
/// List of all SCM packages that can be fetched by this instance
protected Package[Repository] scm;

this()
{
NativePath pkg = NativePath("/tmp/dub-testsuite-nonexistant/packages/");
Expand Down Expand Up @@ -224,6 +227,43 @@ package class TestPackageManager : PackageManager
return null;
}

/**
* Re-Implementation of `loadSCMPackage`.
*
* The base implementation will do a `git` clone, which we would like to avoid.
* Instead, we allow unittests to explicitly define what packages should be
* reachable in a given test.
*/
public override Package loadSCMPackage(string name, Repository repo)
{
import std.string : chompPrefix;

// We're trying to match `loadGitPackage` as much as possible
if (!repo.ref_.startsWith("~") && !repo.ref_.isGitHash)
return null;

string gitReference = repo.ref_.chompPrefix("~");
NativePath destination = this.getPackagePath(PlacementLocation.user, name, repo.ref_);
destination ~= name;
destination.endsWithSlash = true;

foreach (p; getPackageIterator(name))
if (p.path == destination)
return p;

return this.loadSCMRepository(name, repo);
}

/// The private part of `loadSCMPackage`
protected Package loadSCMRepository(string name, Repository repo)
{
if (auto prepo = repo in this.scm) {
this.add(*prepo);
return *prepo;
}
return null;
}

/**
* Adds a `Package` to this `PackageManager`
*
Expand All @@ -238,6 +278,12 @@ package class TestPackageManager : PackageManager
this.m_internal.fromPath ~= pkg;
return pkg;
}

/// Add a reachable SCM package to this `PackageManager`
public void addTestSCMPackage(Repository repo, Package pkg)
{
this.scm[repo] = pkg;
}
}

/**
Expand Down
50 changes: 50 additions & 0 deletions source/dub/test/other.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*******************************************************************************
Tests that don't fit in existing categories
*******************************************************************************/

module dub.test.others;

version (unittest):

import std.algorithm;
import std.format;
import dub.test.base;

// https://github.com/dlang/dub/issues/2696
unittest
{
const ValidURL = `git+https://example.com/dlang/dub`;
// Taken from a commit in the dub repository
const ValidHash = "54339dff7ce9ec24eda550f8055354f712f15800";
const Template = `{"name": "%s", "dependencies": {
"dep1": { "repository": "%s", "version": "%s" }}}`;

scope dub = new TestDub();
dub.packageManager.addTestSCMPackage(
Repository(ValidURL, ValidHash),
// Note: SCM package are always marked as using `~master`
dub.makeTestPackage(`{ "name": "dep1" }`, Version(`~master`)),
);

// Invalid URL, valid hash
const a = Template.format("a", "git+https://nope.nope", ValidHash);
try
dub.loadPackage(dub.addTestPackage(a, Version("1.0.0")));
catch (Exception exc)
assert(exc.message.canFind("Unable to fetch"));

// Valid URL, invalid hash
const b = Template.format("b", ValidURL, "invalid");
try
dub.loadPackage(dub.addTestPackage(b, Version("1.0.0")));
catch (Exception exc)
assert(exc.message.canFind("Unable to fetch"));

// Valid URL, valid hash
const c = Template.format("c", ValidURL, ValidHash);
dub.loadPackage(dub.addTestPackage(c, Version("1.0.0")));
assert(dub.project.hasAllDependencies());
assert(dub.project.getDependency("dep1", true), "Missing 'dep1' dependency");
}

0 comments on commit a6cc7a6

Please sign in to comment.