1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14
10 // UNSUPPORTED: no-filesystem
11 // UNSUPPORTED: availability-filesystem-missing
12 
13 // <filesystem>
14 
15 // path temp_directory_path();
16 // path temp_directory_path(error_code& ec);
17 
18 #include <filesystem>
19 #include <memory>
20 #include <cstdlib>
21 #include <cstring>
22 #include <cassert>
23 
24 #include "test_macros.h"
25 #include "filesystem_test_helper.h"
26 namespace fs = std::filesystem;
27 using namespace fs;
28 
PutEnv(std::string var,fs::path value)29 void PutEnv(std::string var, fs::path value) {
30     assert(utils::setenv(var.c_str(), value.string().c_str(), /* overwrite */ 1) == 0);
31 }
32 
UnsetEnv(std::string var)33 void UnsetEnv(std::string var) {
34     assert(utils::unsetenv(var.c_str()) == 0);
35 }
36 
signature_test()37 static void signature_test()
38 {
39     std::error_code ec; ((void)ec);
40     ASSERT_NOT_NOEXCEPT(temp_directory_path());
41     ASSERT_NOT_NOEXCEPT(temp_directory_path(ec));
42 }
43 
basic_tests()44 static void basic_tests()
45 {
46     scoped_test_env env;
47     const path dne = env.make_env_path("dne");
48     const path file = env.create_file("file", 42);
49 #ifdef _WIN32
50     // Windows doesn't support setting perms::none to trigger failures
51     // reading directories; test using a special inaccessible directory
52     // instead.
53     const path inaccessible_dir = GetWindowsInaccessibleDir();
54 #else
55     const path dir_perms = env.create_dir("bad_perms_dir");
56     const path inaccessible_dir = env.create_dir("bad_perms_dir/nested");
57     permissions(dir_perms, perms::none);
58 #endif
59     LIBCPP_ONLY(const std::errc expect_errc = std::errc::not_a_directory);
60     struct TestCase {
61       std::string name;
62       path p;
63     } cases[] = {
64 #ifdef _WIN32
65         {"TMP", env.create_dir("dir1")},
66         {"TEMP", env.create_dir("dir2")},
67         {"USERPROFILE", env.create_dir("dir3")}
68 #else
69         {"TMPDIR", env.create_dir("dir1")},
70         {"TMP", env.create_dir("dir2")},
71         {"TEMP", env.create_dir("dir3")},
72         {"TEMPDIR", env.create_dir("dir4")}
73 #endif
74     };
75     TestCase ignored_cases[] = {
76 #ifdef _WIN32
77         {"TMPDIR", env.create_dir("dir5")},
78         {"TEMPDIR", env.create_dir("dir6")},
79 #else
80         {"USERPROFILE", env.create_dir("dir5")},
81 #endif
82     };
83     for (auto& TC : cases) {
84         PutEnv(TC.name, TC.p);
85     }
86     for (auto& TC : cases) {
87         std::error_code ec = GetTestEC();
88         path ret = temp_directory_path(ec);
89         assert(!ec);
90         assert(ret == TC.p);
91         assert(is_directory(ret));
92 
93         // Set the env variable to a path that does not exist and check
94         // that it fails.
95         PutEnv(TC.name, dne);
96         ec = GetTestEC();
97         ret = temp_directory_path(ec);
98         LIBCPP_ASSERT(ErrorIs(ec, expect_errc));
99         assert(ec != GetTestEC());
100         assert(ec);
101         assert(ret == "");
102 
103         // Set the env variable to point to a file and check that it fails.
104         PutEnv(TC.name, file);
105         ec = GetTestEC();
106         ret = temp_directory_path(ec);
107         LIBCPP_ASSERT(ErrorIs(ec, expect_errc));
108         assert(ec != GetTestEC());
109         assert(ec);
110         assert(ret == "");
111 
112         if (!inaccessible_dir.empty()) {
113             // Set the env variable to point to a dir we can't access
114             PutEnv(TC.name, inaccessible_dir);
115             ec = GetTestEC();
116             ret = temp_directory_path(ec);
117             assert(ErrorIs(ec, std::errc::permission_denied));
118             assert(ret == "");
119         }
120 
121         // Set the env variable to point to a non-existent dir
122         PutEnv(TC.name, TC.p / "does_not_exist");
123         ec = GetTestEC();
124         ret = temp_directory_path(ec);
125         assert(ec != GetTestEC());
126         assert(ec);
127         assert(ret == "");
128 
129         // Finally erase this env variable
130         UnsetEnv(TC.name);
131     }
132     // No env variables are defined
133     path fallback;
134     {
135         std::error_code ec = GetTestEC();
136         path ret = temp_directory_path(ec);
137         assert(!ec);
138 #if defined(_WIN32)
139         // On Windows, the function falls back to the Windows folder.
140         wchar_t win_dir[MAX_PATH];
141         DWORD win_dir_sz = GetWindowsDirectoryW(win_dir, MAX_PATH);
142         assert(win_dir_sz > 0 && win_dir_sz < MAX_PATH);
143         assert(win_dir[win_dir_sz-1] != L'\\');
144         assert(ret == win_dir);
145 #elif defined(__ANDROID__)
146         assert(ret == "/data/local/tmp");
147 #else
148         assert(ret == "/tmp");
149 #endif
150         assert(is_directory(ret));
151         fallback = ret;
152     }
153     for (auto& TC : ignored_cases) {
154         // Check that certain variables are ignored
155         PutEnv(TC.name, TC.p);
156         std::error_code ec = GetTestEC();
157         path ret = temp_directory_path(ec);
158         assert(!ec);
159 
160         // Check that we return the same as above when no vars were defined.
161         assert(ret == fallback);
162 
163         // Finally erase this env variable
164         UnsetEnv(TC.name);
165     }
166 }
167 
main(int,char **)168 int main(int, char**) {
169     signature_test();
170     basic_tests();
171     return 0;
172 }
173