859bc749b0593a83e1c8cd7149f956b620a24a73
braney
  Sat Sep 19 18:14:15 2026 -0700
trashDirTester: pin which file paths the cart accepts

hg/lib/cart.c screens every cart variable that names a server-made file through
these functions, so they decide whether a saved session still works.  Both ways
of being wrong are invisible: a path wrongly refused brings the session back
without its custom track or its region list and says nothing, and a path
wrongly accepted says nothing either.

That is what #38303 cost.  A check shipped in v503 discarded the saved region
list from 583 sessions on the RR and 66 on euro, and hgwdev, code review and
hgwbeta were all clean, because the corpus that shows it is only on the
production central.

Four rules pinned, each of which cost a bug or a review round: only the
configured directory is symlink-resolved and never the path from the cart; the
resolved spelling of that directory is accepted as well as the configured one,
since /userdata on the RR is a symlink and saved sessions hold both spellings;
only an absolute directory is resolved, because a relative one would be
resolved against the caller's working directory; and the acceptance runs one
direction only.  pathIsUnderDir's own edges are here too, including the sibling
directory that merely starts the same way and the name that begins with "..".

The fixture is a directory, a symlink to it and three confs naming the same
place three ways, since hgConfig caches what it read and one process can only
answer for one spelling.  Nothing machine-specific reaches the output.

Watched to fail and then pass: with the pre-#38303 code, which did not resolve
at all, the resolved spelling comes back refused and the diff is that one line.
Recorded as sandbox-ab in utils/testRegistry.

refs #38303, refs #37623, refs #38391

diff --git src/hg/lib/tests/makefile src/hg/lib/tests/makefile
index f63df5ab613..cad98d9069c 100644
--- src/hg/lib/tests/makefile
+++ src/hg/lib/tests/makefile
@@ -1,74 +1,103 @@
 kentSrc = ../../..
 include ../../../inc/common.mk
 
 L += ${MYSQLLIBS} -lm
 MYLIBDIR = ../../../lib/${MACHTYPE}
 MYLIBS =  ${MYLIBDIR}/jkhgap.a ${MYLIBDIR}/jkweb.a
 BIN_DIR = bin/${MACHTYPE}
 
 all: 	${BIN_DIR}/genePredTester \
+	${BIN_DIR}/trashDirTester \
 	${BIN_DIR}/pslReaderTester \
 	${BIN_DIR}/quickLiftTester \
 	${BIN_DIR}/sessionDataTester \
+	${BIN_DIR}/trashDirTester \
 #	${BIN_DIR}/annoGratorTester \
 	${BIN_DIR}/binTest \
 	${BIN_DIR}/customTrackTester \
 	${BIN_DIR}/hgvsTester \
 	${BIN_DIR}/sqlCheck 
 
 ${BIN_DIR}/%: %.c ${MYLIBS}
 	@${MKDIR} ${BIN_DIR}
 	${CC} ${CC_PROG_OPTS} -o $@ $*.c ${MYLIBS} $L
 
 #test: binTest spDbTest hdbTest genePredTest pslReaderTest annoGratorTest customTrackTest hgvsTest
-test: binTest quickLiftTest sessionDataTest spDbTest hdbTest genePredTest pslReaderTest customTrackTest hgvsTest
+test: binTest quickLiftTest sessionDataTest trashDirTest spDbTest hdbTest genePredTest pslReaderTest customTrackTest hgvsTest
 	rm -r output
 	echo tested all
 
 mkdirs:
 	@${MKDIR} output ${BIN_DIR}
 
 loaders:
 	ln -sf ${CGI_BIN}/loader .
 
 spDbTest: ${BIN_DIR}/spDbTest mkdirs
 	${BIN_DIR}/spDbTest sp121210 Q9FFH7 > output/spDbTest
 	${BIN_DIR}/spDbTest sp121210 P29312 >> output/spDbTest
 	diff expected/spDbTest output/spDbTest
 
 hdbTest: ${BIN_DIR}/hdbTest mkdirs
 	${BIN_DIR}/hdbTest > output/hdbTest
 	diff expected/hdbTest output/hdbTest
 
 genePredTest: ${BIN_DIR}/genePredTester mkdirs
 	${MAKE} -f genePredTests.mk test
 
 pslReaderTest: ${BIN_DIR}/pslReaderTester mkdirs
 	${MAKE} -f pslReaderTests.mk test
 
 #annoGratorTest: ${BIN_DIR}/annoGratorTester mkdirs
 #	${MAKE} -f annoGratorTests.mk test
 
 customTrackTest: ${BIN_DIR}/customTrackTester loaders mkdirs
 	${MAKE} -f customTrackTests.mk test BIN_DIR=${BIN_DIR} OUT_DIR=output
 
 hgvsTest: ${BIN_DIR}/hgvsTester mkdirs
 	${MAKE} -f hgvsTests.mk test
 
 binTest: mkdirs ${BIN_DIR}/binTest
 	@./binTest.sh
 
 quickLiftTest: ${BIN_DIR}/quickLiftTester mkdirs
 	${BIN_DIR}/quickLiftTester > output/quickLiftTest
 	diff expected/quickLiftTest output/quickLiftTest
 
 sessionDataTest: ${BIN_DIR}/sessionDataTester mkdirs
 	${BIN_DIR}/sessionDataTester output/sessionData > output/sessionDataTest
 	diff expected/sessionDataTest output/sessionDataTest
 
+# Three runs, because hgConfig caches the config it read, so one process can only answer for
+# one spelling of sessionDataDir.  The fixture is made here rather than in C: a directory, a
+# symlink to it, and three confs naming the same place three ways.  Each conf includes the
+# developer's own so the database settings come along, and is mode 600 because hgConfig
+# refuses a group-readable file whose name begins with a dot -- these do not, but the habit is
+# worth keeping.
+trashDirTest: ${BIN_DIR}/trashDirTester mkdirs
+	rm -rf output/sd
+	${MKDIR} output/sd/real/hgt
+	ln -s real output/sd/link
+	echo "include ${HOME}/.hg.conf" > output/sd/link.conf
+	echo "sessionDataDir=`pwd`/output/sd/link" >> output/sd/link.conf
+	echo "include ${HOME}/.hg.conf" > output/sd/real.conf
+	echo "sessionDataDir=`pwd`/output/sd/real" >> output/sd/real.conf
+	echo "include ${HOME}/.hg.conf" > output/sd/rel.conf
+	echo "sessionDataDir=output/sd/link" >> output/sd/rel.conf
+	HGDB_CONF=output/sd/link.conf ${BIN_DIR}/trashDirTester \
+	    "the symlink, absolute" `pwd`/output/sd/link `pwd`/output/sd/real \
+	    > output/trashDirTest
+	HGDB_CONF=output/sd/real.conf ${BIN_DIR}/trashDirTester \
+	    "the resolved directory, absolute" `pwd`/output/sd/link `pwd`/output/sd/real \
+	    >> output/trashDirTest
+	HGDB_CONF=output/sd/rel.conf ${BIN_DIR}/trashDirTester \
+	    "the symlink, relative" `pwd`/output/sd/link `pwd`/output/sd/real \
+	    >> output/trashDirTest
+	diff expected/trashDirTest output/trashDirTest
+
 sqlCheck: ${BIN_DIR}/sqlCheck mkdirs
 	${MAKE} -f sqlCheck.mk test BIN_DIR=${BIN_DIR} OUT_DIR=output
 
 
 clean:
 	rm -rf *.o bin output *.tmp loader udcCache