Android patch: Deduplicate data finding code in tests.

The code to find the data and testdata directories was needlessly
duplicated across cintltst and intltest, causing pain for porting to
other platforms. Extract all this functionality into ctestfw so it
can be shared across the test suites.

The upstream request is tracked at
https://unicode-org.atlassian.net/browse/ICU-13849.

Bug: 120776993
Test: treehugger
Change-Id: Ib5e69af3921e3c6401e2f975b640b34ed6593012
diff --git a/icu4c/source/test/cintltst/cintltst.c b/icu4c/source/test/cintltst/cintltst.c
index aaa187f..c341d1e 100644
--- a/icu4c/source/test/cintltst/cintltst.c
+++ b/icu4c/source/test/cintltst/cintltst.c
@@ -55,8 +55,6 @@
 static void ctst_freeAll(void);
 #endif
 
-static char* _testDataPath=NULL;
-
 /*
  *  Forward Declarations
  */
@@ -293,139 +291,6 @@
 }
 */
 
-/* returns the path to icu/source/data */
-const char *  ctest_dataSrcDir()
-{
-    static const char *dataSrcDir = NULL;
-
-    if(dataSrcDir) {
-        return dataSrcDir;
-    }
-
-    /* U_TOPSRCDIR is set by the makefiles on UNIXes when building cintltst and intltst
-    //              to point to the top of the build hierarchy, which may or
-    //              may not be the same as the source directory, depending on
-    //              the configure options used.  At any rate,
-    //              set the data path to the built data from this directory.
-    //              The value is complete with quotes, so it can be used
-    //              as-is as a string constant.
-    */
-#if defined (U_TOPSRCDIR)
-    {
-        dataSrcDir = U_TOPSRCDIR  U_FILE_SEP_STRING "data" U_FILE_SEP_STRING;
-    }
-#else
-
-    /* On Windows, the file name obtained from __FILE__ includes a full path.
-     *             This file is "wherever\icu\source\test\cintltst\cintltst.c"
-     *             Change to    "wherever\icu\source\data"
-     */
-    {
-        static char p[sizeof(__FILE__) + 20];
-        char *pBackSlash;
-        int i;
-
-        strcpy(p, __FILE__);
-        /* We want to back over three '\' chars.                            */
-        /*   Only Windows should end up here, so looking for '\' is safe.   */
-        for (i=1; i<=3; i++) {
-            pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
-            if (pBackSlash != NULL) {
-                *pBackSlash = 0;        /* Truncate the string at the '\'   */
-            }
-        }
-
-        if (pBackSlash != NULL) {
-            /* We found and truncated three names from the path.
-             *  Now append "source\data" and set the environment
-             */
-            strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING );
-            dataSrcDir = p;
-        }
-        else {
-            /* __FILE__ on MSVC7 does not contain the directory */
-            FILE *file = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
-            if (file) {
-                fclose(file);
-                dataSrcDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING;
-            }
-            else {
-                dataSrcDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING;
-            }
-        }
-    }
-#endif
-
-    return dataSrcDir;
-
-}
-
-/* returns the path to icu/source/data/out */
-const char *ctest_dataOutDir()
-{
-    static const char *dataOutDir = NULL;
-
-    if(dataOutDir) {
-        return dataOutDir;
-    }
-
-    /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
-    //              to point to the top of the build hierarchy, which may or
-    //              may not be the same as the source directory, depending on
-    //              the configure options used.  At any rate,
-    //              set the data path to the built data from this directory.
-    //              The value is complete with quotes, so it can be used
-    //              as-is as a string constant.
-    */
-#if defined (U_TOPBUILDDIR)
-    {
-        dataOutDir = U_TOPBUILDDIR "data"U_FILE_SEP_STRING"out"U_FILE_SEP_STRING;
-    }
-#else
-
-    /* On Windows, the file name obtained from __FILE__ includes a full path.
-     *             This file is "wherever\icu\source\test\cintltst\cintltst.c"
-     *             Change to    "wherever\icu\source\data"
-     */
-    {
-        static char p[sizeof(__FILE__) + 20];
-        char *pBackSlash;
-        int i;
-
-        strcpy(p, __FILE__);
-        /* We want to back over three '\' chars.                            */
-        /*   Only Windows should end up here, so looking for '\' is safe.   */
-        for (i=1; i<=3; i++) {
-            pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
-            if (pBackSlash != NULL) {
-                *pBackSlash = 0;        /* Truncate the string at the '\'   */
-            }
-        }
-
-        if (pBackSlash != NULL) {
-            /* We found and truncated three names from the path.
-             *  Now append "source\data" and set the environment
-             */
-            strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
-            dataOutDir = p;
-        }
-        else {
-            /* __FILE__ on MSVC7 does not contain the directory */
-            FILE *file = fopen(".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
-            if (file) {
-                fclose(file);
-                dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
-            }
-            else {
-                dataOutDir = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
-            }
-        }
-    }
-#endif
-
-    return dataOutDir;
-}
-
 /*  ctest_setICU_DATA  - if the ICU_DATA environment variable is not already
  *                       set, try to deduce the directory in which ICU was built,
  *                       and set ICU_DATA to "icu/source/data" in that location.
@@ -436,14 +301,7 @@
  *                       tests dynamically load some data.
  */
 void ctest_setICU_DATA() {
-
-    /* No location for the data dir was identifiable.
-     *   Add other fallbacks for the test data location here if the need arises
-     */
-    if (getenv("ICU_DATA") == NULL) {
-        /* If ICU_DATA isn't set, set it to the usual location */
-        u_setDataDirectory(ctest_dataOutDir());
-    }
+    u_setDataDirectory(ctest_dataOutDir());
 }
 
 /*  These tests do cleanup and reinitialize ICU in the course of their operation.
@@ -533,47 +391,8 @@
     return newString;
 }
 
-const char* loadTestData(UErrorCode* err){
-    if( _testDataPath == NULL){
-        const char*      directory=NULL;
-        UResourceBundle* test =NULL;
-        char* tdpath=NULL;
-        const char* tdrelativepath;
-#if defined (U_TOPBUILDDIR)
-        tdrelativepath = "test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"out"U_FILE_SEP_STRING;
-        directory = U_TOPBUILDDIR;
-#else
-        tdrelativepath = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING"out"U_FILE_SEP_STRING;
-        directory= ctest_dataOutDir();
-#endif
-
-        tdpath = (char*) ctst_malloc(sizeof(char) *(( strlen(directory) * strlen(tdrelativepath)) + 10));
-
-
-        /* u_getDataDirectory shoul return \source\data ... set the
-         * directory to ..\source\data\..\test\testdata\out\testdata
-         *
-         * Fallback: When Memory mapped file is built
-         * ..\source\data\out\..\..\test\testdata\out\testdata
-         */
-        strcpy(tdpath, directory);
-        strcat(tdpath, tdrelativepath);
-        strcat(tdpath,"testdata");
-
-
-        test=ures_open(tdpath, "testtypes", err);
-
-        /* Fall back did not succeed either so return */
-        if(U_FAILURE(*err)){
-            *err = U_FILE_ACCESS_ERROR;
-            log_data_err("Could not load testtypes.res in testdata bundle with path %s - %s\n", tdpath, u_errorName(*err));
-            return "";
-        }
-        ures_close(test);
-        _testDataPath = tdpath;
-        return _testDataPath;
-    }
-    return _testDataPath;
+const char* loadTestData(UErrorCode* err) {
+    return ctest_loadTestData(err);
 }
 
 #define CTEST_MAX_TIMEZONE_SIZE 256
@@ -657,7 +476,6 @@
         }
     }
     ctst_allocated = 0;
-    _testDataPath=NULL;
 }
 
 #define VERBOSE_ASSERTIONS
diff --git a/icu4c/source/test/cintltst/sprpdata.c b/icu4c/source/test/cintltst/sprpdata.c
index b3ac72d..bbef6f8 100644
--- a/icu4c/source/test/cintltst/sprpdata.c
+++ b/icu4c/source/test/cintltst/sprpdata.c
@@ -275,19 +275,10 @@
 doStringPrepTest(const char* binFileName, const char* txtFileName, int32_t options, UErrorCode* errorCode){
 
     const char *testdatapath = loadTestData(errorCode);
-    const char *srcdatapath = NULL;
-    const char *relativepath = NULL;
+    const char *srcdatapath = ctest_testDataDir();
     char *filename = NULL;
     UStringPrepProfile* profile = NULL;
 
-#ifdef U_TOPSRCDIR
-    srcdatapath = U_TOPSRCDIR;
-    relativepath = U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
-#else
-    srcdatapath = ctest_dataOutDir();
-    relativepath = ".."U_FILE_SEP_STRING".."U_FILE_SEP_STRING"test"U_FILE_SEP_STRING"testdata"U_FILE_SEP_STRING;
-#endif
-
     profile = usprep_open(testdatapath, binFileName, errorCode);
 
     if(*errorCode == U_FILE_ACCESS_ERROR) {
@@ -297,10 +288,9 @@
         log_err("Failed to load %s data file. Error: %s \n", binFileName, u_errorName(*errorCode));
         return;
     }
-    filename = (char*) malloc(strlen(srcdatapath)+strlen(relativepath)+strlen(txtFileName)+10 );
+    filename = (char*)malloc(strlen(srcdatapath) + strlen(txtFileName) + 1);
     /* open and load the txt file */
     strcpy(filename,srcdatapath);
-    strcat(filename,relativepath);
     strcat(filename,txtFileName);
 
     parseMappings(filename,profile, TRUE,errorCode);
diff --git a/icu4c/source/test/intltest/intltest.cpp b/icu4c/source/test/intltest/intltest.cpp
index 3d6793e..7bb9032 100644
--- a/icu4c/source/test/intltest/intltest.cpp
+++ b/icu4c/source/test/intltest/intltest.cpp
@@ -51,8 +51,6 @@
 #endif
 
 
-static char* _testDataPath=NULL;
-
 // Static list of errors found
 static UnicodeString errorList;
 static void *knownList = NULL; // known issues
@@ -421,69 +419,7 @@
  *                       tests dynamically load some data.
  */
 void IntlTest::setICU_DATA() {
-    const char *original_ICU_DATA = getenv("ICU_DATA");
-
-    if (original_ICU_DATA != NULL && *original_ICU_DATA != 0) {
-        /*  If the user set ICU_DATA, don't second-guess the person. */
-        return;
-    }
-
-    // U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
-    //              to point to the top of the build hierarchy, which may or
-    //              may not be the same as the source directory, depending on
-    //              the configure options used.  At any rate,
-    //              set the data path to the built data from this directory.
-    //              The value is complete with quotes, so it can be used
-    //              as-is as a string constant.
-
-#if defined (U_TOPBUILDDIR)
-    {
-        static char env_string[] = U_TOPBUILDDIR "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
-        u_setDataDirectory(env_string);
-        return;
-    }
-
-#else
-    // Use #else so we don't get compiler warnings due to the return above.
-
-    /* On Windows, the file name obtained from __FILE__ includes a full path.
-     *             This file is "wherever\icu\source\test\cintltst\cintltst.c"
-     *             Change to    "wherever\icu\source\data"
-     */
-    {
-        char p[sizeof(__FILE__) + 10];
-        char *pBackSlash;
-        int i;
-
-        strcpy(p, __FILE__);
-        /* We want to back over three '\' chars.                            */
-        /*   Only Windows should end up here, so looking for '\' is safe.   */
-        for (i=1; i<=3; i++) {
-            pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
-            if (pBackSlash != NULL) {
-                *pBackSlash = 0;        /* Truncate the string at the '\'   */
-            }
-        }
-
-        if (pBackSlash != NULL) {
-            /* We found and truncated three names from the path.
-             *  Now append "source\data" and set the environment
-             */
-            strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
-            u_setDataDirectory(p);     /*  p is "ICU_DATA=wherever\icu\source\data"    */
-            return;
-        }
-        else {
-            /* __FILE__ on MSVC7 does not contain the directory */
-            u_setDataDirectory(".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
-            return;
-        }
-    }
-#endif
-
-    /* No location for the data dir was identifiable.
-     *   Add other fallbacks for the test data location here if the need arises
-     */
+    u_setDataDirectory(ctest_dataOutDir());
 }
 
 
@@ -1515,9 +1451,6 @@
     CalendarTimeZoneTest::cleanup();
 #endif
 
-    free(_testDataPath);
-    _testDataPath = 0;
-
     Locale lastDefaultLocale;
     if (originalLocale != lastDefaultLocale) {
         major.errln("FAILURE: A test changed the default locale without resetting it.");
@@ -1589,42 +1522,7 @@
 }
 
 const char* IntlTest::loadTestData(UErrorCode& err){
-    if( _testDataPath == NULL){
-        const char*      directory=NULL;
-        UResourceBundle* test =NULL;
-        char* tdpath=NULL;
-        const char* tdrelativepath;
-
-#if defined (U_TOPBUILDDIR)
-        tdrelativepath = "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
-        directory = U_TOPBUILDDIR;
-#else
-        tdrelativepath = ".." U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
-        directory = pathToDataDirectory();
-#endif
-
-        tdpath = (char*) malloc(sizeof(char) *(( strlen(directory) * strlen(tdrelativepath)) + 100));
-
-
-        /* u_getDataDirectory shoul return \source\data ... set the
-         * directory to ..\source\data\..\test\testdata\out\testdata
-         */
-        strcpy(tdpath, directory);
-        strcat(tdpath, tdrelativepath);
-        strcat(tdpath,"testdata");
-
-        test=ures_open(tdpath, "testtypes", &err);
-
-        if(U_FAILURE(err)){
-            err = U_FILE_ACCESS_ERROR;
-            it_dataerrln((UnicodeString)"Could not load testtypes.res in testdata bundle with path " + tdpath + (UnicodeString)" - " + u_errorName(err));
-            return "";
-        }
-        ures_close(test);
-        _testDataPath = tdpath;
-        return _testDataPath;
-    }
-    return _testDataPath;
+    return ctest_loadTestData(&err);
 }
 
 const char* IntlTest::getTestDataPath(UErrorCode& err) {
@@ -1633,23 +1531,7 @@
 
 /* Returns the path to icu/source/test/testdata/ */
 const char *IntlTest::getSourceTestData(UErrorCode& /*err*/) {
-    const char *srcDataDir = NULL;
-#ifdef U_TOPSRCDIR
-    srcDataDir = U_TOPSRCDIR U_FILE_SEP_STRING"test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING;
-#else
-    srcDataDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING;
-    FILE *f = fopen(".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING "rbbitst.txt", "r");
-    if (f) {
-        /* We're in icu/source/test/intltest/ */
-        fclose(f);
-    }
-    else {
-        /* We're in icu/source/test/intltest/Platform/(Debug|Release) */
-        srcDataDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING
-                     "test" U_FILE_SEP_STRING "testdata" U_FILE_SEP_STRING;
-    }
-#endif
-    return srcDataDir;
+    return ctest_testDataDir();
 }
 
 char *IntlTest::getUnidataPath(char path[]) {
@@ -1694,72 +1576,10 @@
     return NULL;
 }
 
-const char* IntlTest::fgDataDir = NULL;
-
 /* returns the path to icu/source/data */
 const char *  IntlTest::pathToDataDirectory()
 {
-
-    if(fgDataDir != NULL) {
-        return fgDataDir;
-    }
-
-    /* U_TOPSRCDIR is set by the makefiles on UNIXes when building cintltst and intltst
-    //              to point to the top of the build hierarchy, which may or
-    //              may not be the same as the source directory, depending on
-    //              the configure options used.  At any rate,
-    //              set the data path to the built data from this directory.
-    //              The value is complete with quotes, so it can be used
-    //              as-is as a string constant.
-    */
-#if defined (U_TOPSRCDIR)
-    {
-        fgDataDir = U_TOPSRCDIR  U_FILE_SEP_STRING "data" U_FILE_SEP_STRING;
-    }
-#else
-
-    /* On Windows, the file name obtained from __FILE__ includes a full path.
-     *             This file is "wherever\icu\source\test\cintltst\cintltst.c"
-     *             Change to    "wherever\icu\source\data"
-     */
-    {
-        static char p[sizeof(__FILE__) + 10];
-        char *pBackSlash;
-        int i;
-
-        strcpy(p, __FILE__);
-        /* We want to back over three '\' chars.                            */
-        /*   Only Windows should end up here, so looking for '\' is safe.   */
-        for (i=1; i<=3; i++) {
-            pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
-            if (pBackSlash != NULL) {
-                *pBackSlash = 0;        /* Truncate the string at the '\'   */
-            }
-        }
-
-        if (pBackSlash != NULL) {
-            /* We found and truncated three names from the path.
-            *  Now append "source\data" and set the environment
-            */
-            strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING );
-            fgDataDir = p;
-        }
-        else {
-            /* __FILE__ on MSVC7 does not contain the directory */
-            FILE *file = fopen(".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
-            if (file) {
-                fclose(file);
-                fgDataDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "data" U_FILE_SEP_STRING;
-            }
-            else {
-                fgDataDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "data" U_FILE_SEP_STRING;
-            }
-        }
-    }
-#endif
-
-    return fgDataDir;
-
+    return ctest_dataSrcDir();
 }
 
 /*
diff --git a/icu4c/source/test/intltest/intltest.h b/icu4c/source/test/intltest/intltest.h
index 37227ba..902f721 100644
--- a/icu4c/source/test/intltest/intltest.h
+++ b/icu4c/source/test/intltest/intltest.h
@@ -383,7 +383,6 @@
 // static members
 public:
     static IntlTest* gTest;
-    static const char* fgDataDir;
 
 };
 
diff --git a/icu4c/source/test/iotest/iotest.cpp b/icu4c/source/test/iotest/iotest.cpp
index 186d10e..f98e232 100644
--- a/icu4c/source/test/iotest/iotest.cpp
+++ b/icu4c/source/test/iotest/iotest.cpp
@@ -701,72 +701,6 @@
     addStreamTests(root);
 }
 
-/* returns the path to icu/source/data/out */
-static const char *ctest_dataOutDir()
-{
-    static const char *dataOutDir = NULL;
-
-    if(dataOutDir) {
-        return dataOutDir;
-    }
-
-    /* U_TOPBUILDDIR is set by the makefiles on UNIXes when building cintltst and intltst
-    //              to point to the top of the build hierarchy, which may or
-    //              may not be the same as the source directory, depending on
-    //              the configure options used.  At any rate,
-    //              set the data path to the built data from this directory.
-    //              The value is complete with quotes, so it can be used
-    //              as-is as a string constant.
-    */
-#if defined (U_TOPBUILDDIR)
-    {
-        dataOutDir = U_TOPBUILDDIR "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
-    }
-#else
-
-    /* On Windows, the file name obtained from __FILE__ includes a full path.
-     *             This file is "wherever\icu\source\test\cintltst\cintltst.c"
-     *             Change to    "wherever\icu\source\data"
-     */
-    {
-        static char p[sizeof(__FILE__) + 20];
-        char *pBackSlash;
-        int i;
-
-        strcpy(p, __FILE__);
-        /* We want to back over three '\' chars.                            */
-        /*   Only Windows should end up here, so looking for '\' is safe.   */
-        for (i=1; i<=3; i++) {
-            pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
-            if (pBackSlash != NULL) {
-                *pBackSlash = 0;        /* Truncate the string at the '\'   */
-            }
-        }
-
-        if (pBackSlash != NULL) {
-            /* We found and truncated three names from the path.
-             *  Now append "source\data" and set the environment
-             */
-            strcpy(pBackSlash, U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING);
-            dataOutDir = p;
-        }
-        else {
-            /* __FILE__ on MSVC7 does not contain the directory */
-            FILE *file = fopen(".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "Makefile.in", "r");
-            if (file) {
-                fclose(file);
-                dataOutDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
-            }
-            else {
-                dataOutDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING "data" U_FILE_SEP_STRING "out" U_FILE_SEP_STRING;
-            }
-        }
-    }
-#endif
-
-    return dataOutDir;
-}
-
 /*  ctest_setICU_DATA  - if the ICU_DATA environment variable is not already
  *                       set, try to deduce the directory in which ICU was built,
  *                       and set ICU_DATA to "icu/source/data" in that location.
@@ -777,14 +711,7 @@
  *                       tests dynamically load some data.
  */
 static void ctest_setICU_DATA() {
-
-    /* No location for the data dir was identifiable.
-     *   Add other fallbacks for the test data location here if the need arises
-     */
-    if (getenv("ICU_DATA") == NULL) {
-        /* If ICU_DATA isn't set, set it to the usual location */
-        u_setDataDirectory(ctest_dataOutDir());
-    }
+    u_setDataDirectory(ctest_dataOutDir());
 }
 
 U_CDECL_BEGIN
diff --git a/icu4c/source/tools/ctestfw/Makefile.in b/icu4c/source/tools/ctestfw/Makefile.in
index 2560092..a1339e7 100644
--- a/icu4c/source/tools/ctestfw/Makefile.in
+++ b/icu4c/source/tools/ctestfw/Makefile.in
@@ -46,7 +46,7 @@
 CXXFLAGS += $(LIBCXXFLAGS)
 
 CPPFLAGS += -I$(top_srcdir)/common -I$(top_srcdir)/i18n -I$(srcdir)/../toolutil -I$(srcdir) $(LIBCPPFLAGS) $(CPPFLAGSCTESTFW)
-DEFS += -DT_CTEST_IMPLEMENTATION
+DEFS += -DT_CTEST_IMPLEMENTATION -D'U_TOPSRCDIR="$(top_srcdir)/"'
 LDFLAGS += $(LDFLAGSCTESTFW)
 LIBS = $(LIBICUTOOLUTIL) $(LIBICUI18N) $(LIBICUUC) $(DEFAULT_LIBS)
 
diff --git a/icu4c/source/tools/ctestfw/ctest.c b/icu4c/source/tools/ctestfw/ctest.c
index 5df32a9..ec840e9 100644
--- a/icu4c/source/tools/ctestfw/ctest.c
+++ b/icu4c/source/tools/ctestfw/ctest.c
@@ -8,15 +8,28 @@
 *
 ********************************************************************************
 */
+#include "unicode/ctest.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #include <stdarg.h>
 #include <ctype.h>
+#include <limits.h>
+
+#if defined(__linux__)
+#include <features.h>
+#endif
+
+#if defined(__GLIBC__)
+// Glibc's PATH_MAX is not in limits.h.
+#include <linux/limits.h>
+#endif
 
 #include "unicode/utrace.h"
 #include "unicode/uclean.h"
+#include "unicode/ures.h"
 #include "putilimp.h"
 #include "udbgutil.h"
 
@@ -41,6 +54,12 @@
 #define SHOW_TIMES 1
 #endif
 
+#if defined(_WIN32)
+#define PATH_MAX MAX_PATH
+#elif !defined(PATH_MAX)
+#define PATH_MAX 256  // The minimum value allowed by POSIX.
+#endif
+
 struct TestNode
 {
   void (*test)(void);
@@ -1309,4 +1328,138 @@
   return 0;
 }
 
+static const char* ctest_icuSrcDir(void) {
+    static const char* srcDir = NULL;
 
+    if (srcDir) {
+        return srcDir;
+    }
+
+#if defined(U_TOPSRCDIR)
+    /* U_TOPSRCDIR is set by the makefiles on UNIXes when building cintltst and
+     * intltst to point to the top of the build hierarchy, which may or may not
+     * be the same as the source directory, depending on the configure options
+     * used.  At any rate, set the data path to the built data from this
+     * directory.  The value is complete with quotes, so it can be used as-is as
+     * a string constant.
+     */
+    srcDir = U_TOPSRCDIR U_FILE_SEP_STRING;
+#elif defined(_WIN32)
+    /* On Windows, the file name obtained from __FILE__ includes a full path.
+     * This file is "wherever\icu\source\test\cintltst\cintltst.c" Change to
+     * "wherever\icu\source\data"
+     */
+    static char p[sizeof(__FILE__) + 20];
+    char* pBackSlash;
+    int i;
+
+    strcpy(p, __FILE__);
+    /* We want to back over three '\' chars.                            */
+    /*   Only Windows should end up here, so looking for '\' is safe.   */
+    for (i=1; i<=3; i++) {
+        pBackSlash = strrchr(p, U_FILE_SEP_CHAR);
+        if (pBackSlash != NULL) {
+            *pBackSlash = 0;        /* Truncate the string at the '\'   */
+        }
+    }
+
+    if (pBackSlash != NULL) {
+        /* We found and truncated three names from the path.
+         *  Now append "source\data" and set the environment
+         */
+        strcpy(pBackSlash, U_FILE_SEP_STRING);
+        srcDir = p;
+    }
+    else {
+        /* __FILE__ on MSVC7 does not contain the directory */
+        FILE* file = fopen(".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING
+                           "runConfigureICU",
+                           "r");
+        if (file) {
+            fclose(file);
+            srcDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING;
+        }
+        else {
+          srcDir = ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING
+                   ".." U_FILE_SEP_STRING ".." U_FILE_SEP_STRING;
+        }
+    }
+#else
+#error ctest_icuSrcDir not implemented on this platform.
+#endif
+
+    return srcDir;
+}
+
+const char* T_CTEST_EXPORT2
+ctest_dataSrcDir(void) {
+    static char path[PATH_MAX];
+
+    if (path[0]) {
+        return path;
+    }
+
+    snprintf(path, sizeof(path), "%sdata%s", ctest_icuSrcDir(),
+             U_FILE_SEP_STRING);
+    return path;
+}
+
+const char* T_CTEST_EXPORT2
+ctest_dataOutDir(void) {
+    static char path[PATH_MAX];
+
+    if (path[0]) {
+        return path;
+    }
+
+    // Try the ICU_DATA environment variable first. This is the default location
+    // since the user will have explicitly requested it.
+    const char* fromEnv = getenv("ICU_DATA");
+    if (fromEnv != NULL && fromEnv[0] != '\0') {
+        snprintf(path, sizeof(path), "%s%s", fromEnv, U_FILE_SEP_STRING);
+        return path;
+    }
+
+    // But fallback to the source directory if needed.
+    snprintf(path, sizeof(path), "%sout%s", ctest_dataSrcDir(),
+             U_FILE_SEP_STRING);
+    return path;
+}
+
+const char* T_CTEST_EXPORT2
+ctest_testDataDir(void) {
+    static char path[PATH_MAX];
+
+    if (path[0]) {
+        return path;
+    }
+
+    snprintf(path, sizeof(path), "%stest%stestdata%s", ctest_icuSrcDir(),
+             U_FILE_SEP_STRING, U_FILE_SEP_STRING);
+    return path;
+}
+
+const char* T_CTEST_EXPORT2
+ctest_loadTestData(UErrorCode* err) {
+    static char path[PATH_MAX];
+
+    if (path[0]) {
+        return path;
+    }
+
+    snprintf(path, sizeof(path), "%sout%stestdata", ctest_testDataDir(),
+             U_FILE_SEP_STRING);
+
+    UResourceBundle* test = ures_open(path, "testtypes", err);
+    if (U_FAILURE(*err)) {
+        *err = U_FILE_ACCESS_ERROR;
+        log_data_err(
+            "Could not load testtypes.res in testdata bundle with path %s - "
+            "%s\n",
+            path, u_errorName(*err));
+        return "";
+    }
+    ures_close(test);
+
+    return path;
+}
diff --git a/icu4c/source/tools/ctestfw/unicode/ctest.h b/icu4c/source/tools/ctestfw/unicode/ctest.h
index c950bad..5a5e42e 100644
--- a/icu4c/source/tools/ctestfw/unicode/ctest.h
+++ b/icu4c/source/tools/ctestfw/unicode/ctest.h
@@ -311,4 +311,45 @@
 T_CTEST_EXPORT2
 ctest_xml_testcase(const char *classname, const char *name, const char *time, const char *failMsg);
 
+/**
+ * Returns the path to icu4c/source/data.
+ *
+ * @return The path to icu4c/source/data. Do not free this pointer.
+ */
+T_CTEST_API const char* T_CTEST_EXPORT2
+ctest_dataSrcDir(void);
+
+/**
+ * Returns the path to the ICU data.
+ *
+ * Uses, in order of preference:
+ *
+ * 1. The path in the ICU_DATA environment variable.
+ * 2. icu4c/source/data/out.
+ *
+ * @return The path to the ICU data. Do not free this pointer.
+ */
+T_CTEST_API const char* T_CTEST_EXPORT2
+ctest_dataOutDir(void);
+
+/**
+ * Returns the path to icu4c/source/test/testdata.
+ *
+ * @return The path to icu4c/source/test/testdata. Do not free this pointer.
+ */
+T_CTEST_API const char* T_CTEST_EXPORT2
+ctest_testDataDir(void);
+
+/**
+ * Returns the path to icu4c/source/test/testdata/out/testdata if it is
+ * loadable.
+ *
+ * @param err Out parameter for any errors.
+ * @return The path to icu4c/source/test/testdata/out/testdata if it can be
+ *         loaded, or the empty string if it could not be loaded. Do not free
+ *         this pointer.
+ */
+T_CTEST_API const char* T_CTEST_EXPORT2
+ctest_loadTestData(UErrorCode* err);
+
 #endif