Skip to content

Commit 2e9aa6d

Browse files
Alexis Poltinyalldawson
authored andcommittedMar 9, 2018
Fix project path when path contains a symbolic link
(cherry-picked from 5b2c81)
1 parent 169e918 commit 2e9aa6d

File tree

3 files changed

+80
-15
lines changed

3 files changed

+80
-15
lines changed
 

‎src/core/qgspathresolver.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,14 @@ QString QgsPathResolver::writePath( const QString &src ) const
146146
return src;
147147
}
148148

149-
QFileInfo pfi( mBaseFileName );
150-
QString projPath = pfi.absoluteFilePath();
149+
// Get projPath even if project has not been created yet
150+
QFileInfo pfi( QFileInfo( mBaseFileName ).path() );
151+
QString projPath = pfi.canonicalFilePath();
152+
153+
// If project directory doesn't exit, fallback to absoluteFilePath : symbolic
154+
// links won't be handled correctly, but that's OK as the path is "virtual".
155+
if ( projPath.isEmpty() )
156+
projPath = pfi.absoluteFilePath();
151157

152158
if ( projPath.isEmpty() )
153159
{
@@ -185,12 +191,9 @@ QString QgsPathResolver::writePath( const QString &src ) const
185191
const Qt::CaseSensitivity cs = Qt::CaseSensitive;
186192
#endif
187193

188-
QStringList projElems = mBaseFileName.split( '/', QString::SkipEmptyParts );
194+
QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
189195
QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
190196

191-
// remove project file element
192-
projElems.removeLast();
193-
194197
projElems.removeAll( QStringLiteral( "." ) );
195198
srcElems.removeAll( QStringLiteral( "." ) );
196199

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

208211
if ( n == 0 )
209212
{
210-
// no common parts; might not even by a file
213+
// no common parts; might not even be a file
211214
return src;
212215
}
213216

‎tests/src/core/testqgsproject.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,30 @@ void TestQgsProject::testReadPath()
9999

100100
void TestQgsProject::testPathResolver()
101101
{
102-
QgsPathResolver resolverRel( QStringLiteral( "/home/qgis/test.qgs" ) );
103-
QCOMPARE( resolverRel.writePath( "/home/qgis/file1.txt" ), QString( "./file1.txt" ) );
104-
QCOMPARE( resolverRel.writePath( "/home/qgis/subdir/file1.txt" ), QString( "./subdir/file1.txt" ) );
105-
QCOMPARE( resolverRel.writePath( "/home/file1.txt" ), QString( "../file1.txt" ) );
106-
QCOMPARE( resolverRel.readPath( "./file1.txt" ), QString( "/home/qgis/file1.txt" ) );
107-
QCOMPARE( resolverRel.readPath( "./subdir/file1.txt" ), QString( "/home/qgis/subdir/file1.txt" ) );
108-
QCOMPARE( resolverRel.readPath( "../file1.txt" ), QString( "/home/file1.txt" ) );
109-
QCOMPARE( resolverRel.readPath( "/home/qgis/file1.txt" ), QString( "/home/qgis/file1.txt" ) );
102+
// Test resolver with a non existing file path
103+
QgsPathResolver resolverLegacy( QStringLiteral( "/home/qgis/test.qgs" ) );
104+
QCOMPARE( resolverLegacy.writePath( "/home/qgis/file1.txt" ), QString( "./file1.txt" ) );
105+
QCOMPARE( resolverLegacy.writePath( "/home/qgis/subdir/file1.txt" ), QString( "./subdir/file1.txt" ) );
106+
QCOMPARE( resolverLegacy.writePath( "/home/file1.txt" ), QString( "../file1.txt" ) );
107+
QCOMPARE( resolverLegacy.readPath( "./file1.txt" ), QString( "/home/qgis/file1.txt" ) );
108+
QCOMPARE( resolverLegacy.readPath( "./subdir/file1.txt" ), QString( "/home/qgis/subdir/file1.txt" ) );
109+
QCOMPARE( resolverLegacy.readPath( "../file1.txt" ), QString( "/home/file1.txt" ) );
110+
QCOMPARE( resolverLegacy.readPath( "/home/qgis/file1.txt" ), QString( "/home/qgis/file1.txt" ) );
111+
112+
// Test resolver with existing file path
113+
QTemporaryDir tmpDir;
114+
QString tmpDirName = tmpDir.path();
115+
QDir dir( tmpDirName );
116+
dir.mkpath( tmpDirName + "/home/qgis/" );
117+
118+
QgsPathResolver resolverRel( QString( tmpDirName + "/home/qgis/test.qgs" ) );
119+
QCOMPARE( resolverRel.writePath( tmpDirName + "/home/qgis/file1.txt" ), QString( "./file1.txt" ) );
120+
QCOMPARE( resolverRel.writePath( tmpDirName + "/home/qgis/subdir/file1.txt" ), QString( "./subdir/file1.txt" ) );
121+
QCOMPARE( resolverRel.writePath( tmpDirName + "/home/file1.txt" ), QString( "../file1.txt" ) );
122+
QCOMPARE( resolverRel.readPath( "./file1.txt" ), QString( tmpDirName + "/home/qgis/file1.txt" ) );
123+
QCOMPARE( resolverRel.readPath( "./subdir/file1.txt" ), QString( tmpDirName + "/home/qgis/subdir/file1.txt" ) );
124+
QCOMPARE( resolverRel.readPath( "../file1.txt" ), QString( tmpDirName + "/home/file1.txt" ) );
125+
QCOMPARE( resolverRel.readPath( tmpDirName + "/home/qgis/file1.txt" ), QString( tmpDirName + "/home/qgis/file1.txt" ) );
110126

111127
// test older style relative path - file must exist for this to work
112128
QTemporaryFile tmpFile;

‎tests/src/python/test_qgsproject.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,52 @@ def testRelativePaths(self):
811811

812812
del project
813813

814+
def testSymbolicLinkInProjectPath(self):
815+
"""
816+
Test whether paths to layer sources relative to the project are stored correctly
817+
when project'name contains a symbolic link.
818+
In other words, test if project's and layers' names are correctly resolved.
819+
"""
820+
tmpDir = QTemporaryDir()
821+
tmpFile = "{}/project.qgs".format(tmpDir.path())
822+
copyfile(os.path.join(TEST_DATA_DIR, "points.shp"), os.path.join(tmpDir.path(), "points.shp"))
823+
copyfile(os.path.join(TEST_DATA_DIR, "points.dbf"), os.path.join(tmpDir.path(), "points.dbf"))
824+
copyfile(os.path.join(TEST_DATA_DIR, "points.shx"), os.path.join(tmpDir.path(), "points.shx"))
825+
copyfile(os.path.join(TEST_DATA_DIR, "lines.shp"), os.path.join(tmpDir.path(), "lines.shp"))
826+
copyfile(os.path.join(TEST_DATA_DIR, "lines.dbf"), os.path.join(tmpDir.path(), "lines.dbf"))
827+
copyfile(os.path.join(TEST_DATA_DIR, "lines.shx"), os.path.join(tmpDir.path(), "lines.shx"))
828+
copyfile(os.path.join(TEST_DATA_DIR, "landsat_4326.tif"), os.path.join(tmpDir.path(), "landsat_4326.tif"))
829+
830+
project = QgsProject()
831+
832+
l0 = QgsVectorLayer(os.path.join(tmpDir.path(), "points.shp"), "points", "ogr")
833+
l1 = QgsVectorLayer(os.path.join(tmpDir.path(), "lines.shp"), "lines", "ogr")
834+
l2 = QgsRasterLayer(os.path.join(tmpDir.path(), "landsat_4326.tif"), "landsat", "gdal")
835+
self.assertTrue(l0.isValid())
836+
self.assertTrue(l1.isValid())
837+
self.assertTrue(l2.isValid())
838+
self.assertTrue(project.addMapLayers([l0, l1, l2]))
839+
self.assertTrue(project.write(tmpFile))
840+
del project
841+
842+
# Create symbolic link to previous project
843+
tmpDir2 = QTemporaryDir()
844+
symlinkDir = os.path.join(tmpDir2.path(), "dir")
845+
os.symlink(tmpDir.path(), symlinkDir)
846+
tmpFile = "{}/project.qgs".format(symlinkDir)
847+
848+
# Open project from symmlink and force re-save.
849+
project = QgsProject()
850+
self.assertTrue(project.read(tmpFile))
851+
self.assertTrue(project.write(tmpFile))
852+
del project
853+
854+
with open(tmpFile, 'r') as f:
855+
content = ''.join(f.readlines())
856+
self.assertTrue('source="./lines.shp"' in content)
857+
self.assertTrue('source="./points.shp"' in content)
858+
self.assertTrue('source="./landsat_4326.tif"' in content)
859+
814860

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

0 commit comments

Comments
 (0)
Please sign in to comment.