Skip to content

Commit

Permalink
Merge pull request #6414 from alexispolti/fix-project-path
Browse files Browse the repository at this point in the history
Fix project path when path contains a symbolic link
  • Loading branch information
luipir authored Mar 6, 2018
2 parents 5d52679 + 5b2c81b commit 74d14ae
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 15 deletions.
17 changes: 10 additions & 7 deletions src/core/qgspathresolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,14 @@ QString QgsPathResolver::writePath( const QString &src ) const
return src;
}

QFileInfo pfi( mBaseFileName );
QString projPath = pfi.absoluteFilePath();
// Get projPath even if project has not been created yet
QFileInfo pfi( QFileInfo( mBaseFileName ).path() );
QString projPath = pfi.canonicalFilePath();

// If project directory doesn't exit, fallback to absoluteFilePath : symbolic
// links won't be handled correctly, but that's OK as the path is "virtual".
if ( projPath.isEmpty() )
projPath = pfi.absoluteFilePath();

if ( projPath.isEmpty() )
{
Expand Down Expand Up @@ -185,12 +191,9 @@ QString QgsPathResolver::writePath( const QString &src ) const
const Qt::CaseSensitivity cs = Qt::CaseSensitive;
#endif

QStringList projElems = mBaseFileName.split( '/', QString::SkipEmptyParts );
QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );

// remove project file element
projElems.removeLast();

projElems.removeAll( QStringLiteral( "." ) );
srcElems.removeAll( QStringLiteral( "." ) );

Expand All @@ -207,7 +210,7 @@ QString QgsPathResolver::writePath( const QString &src ) const

if ( n == 0 )
{
// no common parts; might not even by a file
// no common parts; might not even be a file
return src;
}

Expand Down
32 changes: 24 additions & 8 deletions tests/src/core/testqgsproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,30 @@ void TestQgsProject::testReadPath()

void TestQgsProject::testPathResolver()
{
QgsPathResolver resolverRel( QStringLiteral( "/home/qgis/test.qgs" ) );
QCOMPARE( resolverRel.writePath( "/home/qgis/file1.txt" ), QString( "./file1.txt" ) );
QCOMPARE( resolverRel.writePath( "/home/qgis/subdir/file1.txt" ), QString( "./subdir/file1.txt" ) );
QCOMPARE( resolverRel.writePath( "/home/file1.txt" ), QString( "../file1.txt" ) );
QCOMPARE( resolverRel.readPath( "./file1.txt" ), QString( "/home/qgis/file1.txt" ) );
QCOMPARE( resolverRel.readPath( "./subdir/file1.txt" ), QString( "/home/qgis/subdir/file1.txt" ) );
QCOMPARE( resolverRel.readPath( "../file1.txt" ), QString( "/home/file1.txt" ) );
QCOMPARE( resolverRel.readPath( "/home/qgis/file1.txt" ), QString( "/home/qgis/file1.txt" ) );
// Test resolver with a non existing file path
QgsPathResolver resolverLegacy( QStringLiteral( "/home/qgis/test.qgs" ) );
QCOMPARE( resolverLegacy.writePath( "/home/qgis/file1.txt" ), QString( "./file1.txt" ) );
QCOMPARE( resolverLegacy.writePath( "/home/qgis/subdir/file1.txt" ), QString( "./subdir/file1.txt" ) );
QCOMPARE( resolverLegacy.writePath( "/home/file1.txt" ), QString( "../file1.txt" ) );
QCOMPARE( resolverLegacy.readPath( "./file1.txt" ), QString( "/home/qgis/file1.txt" ) );
QCOMPARE( resolverLegacy.readPath( "./subdir/file1.txt" ), QString( "/home/qgis/subdir/file1.txt" ) );
QCOMPARE( resolverLegacy.readPath( "../file1.txt" ), QString( "/home/file1.txt" ) );
QCOMPARE( resolverLegacy.readPath( "/home/qgis/file1.txt" ), QString( "/home/qgis/file1.txt" ) );

// Test resolver with existing file path
QTemporaryDir tmpDir;
QString tmpDirName = tmpDir.path();
QDir dir( tmpDirName );
dir.mkpath( tmpDirName + "/home/qgis/" );

QgsPathResolver resolverRel( QString( tmpDirName + "/home/qgis/test.qgs" ) );
QCOMPARE( resolverRel.writePath( tmpDirName + "/home/qgis/file1.txt" ), QString( "./file1.txt" ) );
QCOMPARE( resolverRel.writePath( tmpDirName + "/home/qgis/subdir/file1.txt" ), QString( "./subdir/file1.txt" ) );
QCOMPARE( resolverRel.writePath( tmpDirName + "/home/file1.txt" ), QString( "../file1.txt" ) );
QCOMPARE( resolverRel.readPath( "./file1.txt" ), QString( tmpDirName + "/home/qgis/file1.txt" ) );
QCOMPARE( resolverRel.readPath( "./subdir/file1.txt" ), QString( tmpDirName + "/home/qgis/subdir/file1.txt" ) );
QCOMPARE( resolverRel.readPath( "../file1.txt" ), QString( tmpDirName + "/home/file1.txt" ) );
QCOMPARE( resolverRel.readPath( tmpDirName + "/home/qgis/file1.txt" ), QString( tmpDirName + "/home/qgis/file1.txt" ) );

// test older style relative path - file must exist for this to work
QTemporaryFile tmpFile;
Expand Down
46 changes: 46 additions & 0 deletions tests/src/python/test_qgsproject.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,52 @@ def testRelativePaths(self):

del project

def testSymbolicLinkInProjectPath(self):
"""
Test whether paths to layer sources relative to the project are stored correctly
when project'name contains a symbolic link.
In other words, test if project's and layers' names are correctly resolved.
"""
tmpDir = QTemporaryDir()
tmpFile = "{}/project.qgs".format(tmpDir.path())
copyfile(os.path.join(TEST_DATA_DIR, "points.shp"), os.path.join(tmpDir.path(), "points.shp"))
copyfile(os.path.join(TEST_DATA_DIR, "points.dbf"), os.path.join(tmpDir.path(), "points.dbf"))
copyfile(os.path.join(TEST_DATA_DIR, "points.shx"), os.path.join(tmpDir.path(), "points.shx"))
copyfile(os.path.join(TEST_DATA_DIR, "lines.shp"), os.path.join(tmpDir.path(), "lines.shp"))
copyfile(os.path.join(TEST_DATA_DIR, "lines.dbf"), os.path.join(tmpDir.path(), "lines.dbf"))
copyfile(os.path.join(TEST_DATA_DIR, "lines.shx"), os.path.join(tmpDir.path(), "lines.shx"))
copyfile(os.path.join(TEST_DATA_DIR, "landsat_4326.tif"), os.path.join(tmpDir.path(), "landsat_4326.tif"))

project = QgsProject()

l0 = QgsVectorLayer(os.path.join(tmpDir.path(), "points.shp"), "points", "ogr")
l1 = QgsVectorLayer(os.path.join(tmpDir.path(), "lines.shp"), "lines", "ogr")
l2 = QgsRasterLayer(os.path.join(tmpDir.path(), "landsat_4326.tif"), "landsat", "gdal")
self.assertTrue(l0.isValid())
self.assertTrue(l1.isValid())
self.assertTrue(l2.isValid())
self.assertTrue(project.addMapLayers([l0, l1, l2]))
self.assertTrue(project.write(tmpFile))
del project

# Create symbolic link to previous project
tmpDir2 = QTemporaryDir()
symlinkDir = os.path.join(tmpDir2.path(), "dir")
os.symlink(tmpDir.path(), symlinkDir)
tmpFile = "{}/project.qgs".format(symlinkDir)

# Open project from symmlink and force re-save.
project = QgsProject()
self.assertTrue(project.read(tmpFile))
self.assertTrue(project.write(tmpFile))
del project

with open(tmpFile, 'r') as f:
content = ''.join(f.readlines())
self.assertTrue('source="./lines.shp"' in content)
self.assertTrue('source="./points.shp"' in content)
self.assertTrue('source="./landsat_4326.tif"' in content)


if __name__ == '__main__':
unittest.main()

0 comments on commit 74d14ae

Please sign in to comment.