this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <unistd.h>
3
4#include <climits>
5#include <cstdlib>
6#include <string>
7
8#include "Python.h"
9#include "gtest/gtest.h"
10
11#include "capi-fixture.h"
12#include "capi-testing.h"
13
14namespace py {
15namespace testing {
16
17using SysModuleExtensionApiTest = ExtensionApi;
18
19TEST_F(SysModuleExtensionApiTest, GetObjectWithNonExistentNameReturnsNull) {
20 EXPECT_EQ(PySys_GetObject("foo_bar_not_a_real_name"), nullptr);
21 EXPECT_EQ(PyErr_Occurred(), nullptr);
22}
23
24TEST_F(SysModuleExtensionApiTest, GetObjectReturnsValueFromSysModule) {
25 PyRun_SimpleString(R"(
26import sys
27sys.foo = 'bar'
28)");
29 PyObject* result = PySys_GetObject("foo"); // borrowed reference
30 EXPECT_EQ(PyErr_Occurred(), nullptr);
31 EXPECT_TRUE(isUnicodeEqualsCStr(result, "bar"));
32}
33
34TEST_F(SysModuleExtensionApiTest, GetSizeOfPropagatesException) {
35 PyRun_SimpleString(R"(
36class C:
37 def __sizeof__(self): raise Exception()
38o = C()
39)");
40 PyObjectPtr object(mainModuleGet("o"));
41 EXPECT_EQ(_PySys_GetSizeOf(object), static_cast<size_t>(-1));
42 ASSERT_NE(PyErr_Occurred(), nullptr);
43 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_Exception));
44}
45
46TEST_F(SysModuleExtensionApiTest, GetSizeOfReturnsDunderSizeOfPyro) {
47 PyRun_SimpleString(R"(
48class C:
49 def __sizeof__(self): return 10
50o = C()
51)");
52 PyObjectPtr object(mainModuleGet("o"));
53 EXPECT_EQ(_PySys_GetSizeOf(object), size_t{10});
54 EXPECT_EQ(PyErr_Occurred(), nullptr);
55}
56
57TEST_F(SysModuleExtensionApiTest, GetSizeOfWithIntSubclassReturnsIntPyro) {
58 PyRun_SimpleString(R"(
59class N(int): pass
60class C:
61 def __sizeof__(self): return N(10)
62o = C()
63)");
64 PyObjectPtr object(mainModuleGet("o"));
65 EXPECT_EQ(_PySys_GetSizeOf(object), size_t{10});
66 EXPECT_EQ(PyErr_Occurred(), nullptr);
67}
68
69TEST_F(SysModuleExtensionApiTest, WriteStdout) {
70 CaptureStdStreams streams;
71 PySys_WriteStdout("Hello, %s!", "World");
72 EXPECT_EQ(streams.out(), "Hello, World!");
73 EXPECT_EQ(streams.err(), "");
74}
75
76TEST_F(
77 SysModuleExtensionApiTest,
78 WriteStdoutCallsSysStdoutWriteOnExceptionWritesToFallbackAndClearsError) {
79 PyRun_SimpleString(R"(
80import sys
81x = 7
82class C:
83 def write(self, text):
84 global x
85 x = 42
86 raise UserWarning()
87
88sys.stdout = C()
89)");
90 CaptureStdStreams streams;
91 PySys_WriteStdout("a");
92 EXPECT_EQ(streams.out(), "a");
93 EXPECT_EQ(streams.err(), "");
94 ASSERT_EQ(PyErr_Occurred(), nullptr);
95 PyObjectPtr x(mainModuleGet("x"));
96 EXPECT_EQ(PyLong_AsLong(x), 42);
97}
98
99TEST_F(SysModuleExtensionApiTest, WriteStdoutWithSysStdoutNoneWritesToStdout) {
100 PyRun_SimpleString(R"(
101import sys
102sys.stdout = None
103)");
104 CaptureStdStreams streams;
105 PySys_WriteStdout("Hello");
106 EXPECT_EQ(streams.out(), "Hello");
107 EXPECT_EQ(streams.err(), "");
108}
109
110TEST_F(SysModuleExtensionApiTest, WriteStdoutWithoutSysStdoutWritesToStdout) {
111 PyRun_SimpleString(R"(
112import sys
113del sys.stdout
114)");
115 CaptureStdStreams streams;
116 PySys_WriteStdout("Konnichiwa\n");
117 EXPECT_EQ(streams.out(), "Konnichiwa\n");
118 EXPECT_EQ(streams.err(), "");
119}
120
121TEST_F(SysModuleExtensionApiTest, WriteStdoutTruncatesLongOutput) {
122 const size_t max_out_len = 1000;
123 std::string long_str;
124 for (int i = 0; i < 100; i++) {
125 long_str += "0123456789";
126 }
127 ASSERT_EQ(long_str.size(), max_out_len);
128
129 CaptureStdStreams streams;
130 PySys_WriteStdout("%s hello", long_str.c_str());
131 EXPECT_EQ(streams.out(), long_str + "... truncated");
132 EXPECT_EQ(streams.err(), "");
133}
134
135TEST_F(SysModuleExtensionApiTest, WriteStderr) {
136 CaptureStdStreams streams;
137 PySys_WriteStderr("2 + 2 = %d", 4);
138 EXPECT_EQ(streams.out(), "");
139 EXPECT_EQ(streams.err(), "2 + 2 = 4");
140}
141
142TEST_F(SysModuleExtensionApiTest,
143 SetArgvWithEmptyArgvPopulatesSysArgvAndSysPathWithEmptyString) {
144 wchar_t arg0[] = L"python";
145 wchar_t* wargv[] = {arg0};
146 PySys_SetArgv(0, wargv + 1);
147
148 PyObject* argv = PySys_GetObject("argv");
149 EXPECT_EQ(PyList_Size(argv), 1);
150 EXPECT_TRUE(isUnicodeEqualsCStr(PyList_GetItem(argv, 0), ""));
151 PyObject* sys_path = PySys_GetObject("path");
152 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
153 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, ""));
154}
155
156TEST_F(SysModuleExtensionApiTest,
157 SetArgvWithScriptAndArgsPopulatesSysArgvWithScriptAndArgs) {
158 wchar_t arg0[] = L"script.py";
159 wchar_t arg1[] = L"3";
160 wchar_t arg2[] = L"2";
161 wchar_t* wargv[] = {arg0, arg1, arg2};
162 PySys_SetArgv(3, wargv);
163 PyObject* argv = PySys_GetObject("argv");
164 EXPECT_EQ(PyList_Size(argv), 3);
165 EXPECT_TRUE(isUnicodeEqualsCStr(PyList_GetItem(argv, 0), "script.py"));
166 EXPECT_TRUE(isUnicodeEqualsCStr(PyList_GetItem(argv, 1), "3"));
167 EXPECT_TRUE(isUnicodeEqualsCStr(PyList_GetItem(argv, 2), "2"));
168}
169
170TEST_F(SysModuleExtensionApiTest,
171 SetArgvWithModuleArgInsertsWorkingDirectoryIntoSysPath) {
172 wchar_t arg0[] = L"-m";
173 wchar_t* wargv[] = {arg0};
174 PySys_SetArgv(1, wargv);
175
176 char cwd[PATH_MAX] = {0};
177 ASSERT_NE(::getcwd(cwd, PATH_MAX), nullptr);
178
179 PyObject* sys_path = PySys_GetObject("path");
180 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
181 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, cwd));
182}
183
184TEST_F(SysModuleExtensionApiTest,
185 SetArgvWithCommandArgInsertsEmptyStringIntoSysPath) {
186 wchar_t arg0[] = L"-c";
187 wchar_t* wargv[] = {arg0};
188 PySys_SetArgv(1, wargv);
189
190 PyObject* sys_path = PySys_GetObject("path");
191 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
192 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, ""));
193}
194
195TEST_F(SysModuleExtensionApiTest,
196 SetArgvWithAbsolutePathInsertsPathIntoSysPath) {
197 TempDirectory tmpdir;
198 std::string tmpfile = tmpdir.path() + "scriptfile.py";
199 std::string touch = "touch " + tmpfile;
200 int result = std::system(touch.c_str());
201 ASSERT_EQ(result, 0);
202
203 wchar_t arg0[] = L"python";
204 wchar_t* arg1 = Py_DecodeLocale(tmpfile.c_str(), nullptr);
205 wchar_t* wargv[] = {arg0, arg1};
206 PySys_SetArgv(1, wargv + 1);
207
208 PyObject* sys_path = PySys_GetObject("path");
209 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
210 char* abs_realpath = ::realpath(tmpdir.path().c_str(), nullptr);
211 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, abs_realpath));
212
213 PyMem_RawFree(arg1);
214 std::free(abs_realpath);
215}
216
217TEST_F(SysModuleExtensionApiTest, SetArgvWithLocalPathAddsPathStringToSysPath) {
218 TempDirectory tmpdir;
219 char* cwd = ::getcwd(nullptr, 0);
220 int result = ::chdir(tmpdir.path().c_str());
221 ASSERT_EQ(result, 0);
222
223 std::string tmpfile = "scriptfile.py";
224 std::string touch = "touch " + tmpfile;
225 result = std::system(touch.c_str());
226 ASSERT_EQ(result, 0);
227
228 wchar_t arg0[] = L"python";
229 wchar_t* arg1 = Py_DecodeLocale(tmpfile.c_str(), nullptr);
230 wchar_t* wargv[] = {arg0, arg1};
231 PySys_SetArgv(1, wargv + 1);
232
233 PyObject* sys_path = PySys_GetObject("path");
234 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
235 char* abs_path = ::realpath(tmpdir.path().c_str(), nullptr);
236 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, abs_path));
237
238 result = ::chdir(cwd);
239 ASSERT_EQ(result, 0);
240 PyMem_RawFree(arg1);
241 std::free(abs_path);
242 std::free(cwd);
243}
244
245TEST_F(SysModuleExtensionApiTest, SetArgvWithRelativePathAddsPathToSysPath) {
246 TempDirectory tmpdir;
247 char* cwd = ::getcwd(nullptr, 0);
248 int result = ::chdir(tmpdir.path().c_str());
249 ASSERT_EQ(result, 0);
250
251 std::string relative_path = "./";
252 std::string tmpfile = relative_path + "scriptfile.py";
253 std::string touch = "touch " + tmpfile;
254 result = std::system(touch.c_str());
255 ASSERT_EQ(result, 0);
256
257 wchar_t arg0[] = L"python";
258 wchar_t* arg1 = Py_DecodeLocale(tmpfile.c_str(), nullptr);
259 wchar_t* wargv[] = {arg0, arg1};
260 PySys_SetArgv(1, wargv + 1);
261
262 PyObject* sys_path = PySys_GetObject("path");
263 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
264 char* abs_path = ::realpath(relative_path.c_str(), nullptr);
265 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, abs_path));
266
267 result = ::chdir(cwd);
268 ASSERT_EQ(result, 0);
269 PyMem_RawFree(arg1);
270 std::free(cwd);
271 std::free(abs_path);
272}
273
274TEST_F(SysModuleExtensionApiTest, SetArgvWithRootPathInsertsRootIntoSysPath) {
275 wchar_t arg0[] = L"python";
276 wchar_t arg1[] = L"/root_script.py";
277 wchar_t* wargv[] = {arg0, arg1};
278 PySys_SetArgv(1, wargv + 1);
279
280 PyObject* sys_path = PySys_GetObject("path");
281 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
282 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, "/"));
283}
284
285TEST_F(SysModuleExtensionApiTest,
286 SetArgvWithLocalSymlinkInsertsPathIntoSysPath) {
287 TempDirectory tmpdir;
288 std::string tmpfile = tmpdir.path() + "scriptfile.py";
289 std::string linkfile = tmpdir.path() + "scriptlink.py";
290 std::string touch = "touch " + tmpfile;
291 int result = std::system(touch.c_str());
292 ASSERT_EQ(result, 0);
293 std::string ln = "ln -s scriptfile.py " + linkfile;
294 result = std::system(ln.c_str());
295 ASSERT_EQ(result, 0);
296
297 wchar_t arg0[] = L"python";
298 wchar_t* arg1 = Py_DecodeLocale(linkfile.c_str(), nullptr);
299 wchar_t* wargv[] = {arg0, arg1};
300 PySys_SetArgv(1, wargv + 1);
301
302 PyObject* sys_path = PySys_GetObject("path");
303 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
304 char* abs_realpath = ::realpath(tmpdir.path().c_str(), nullptr);
305 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, abs_realpath));
306
307 PyMem_RawFree(arg1);
308 std::free(abs_realpath);
309}
310
311TEST_F(SysModuleExtensionApiTest,
312 SetArgvWithAbsoluteSymlinkInsertsPathIntoSysPath) {
313 TempDirectory tmpdir1;
314 TempDirectory tmpdir2;
315 std::string tmpfile = tmpdir1.path() + "scriptfile.py";
316 std::string linkfile = tmpdir2.path() + "scriptlink.py";
317 std::string touch = "touch " + tmpfile;
318 int result = std::system(touch.c_str());
319 ASSERT_EQ(result, 0);
320 std::string ln = "ln -s " + tmpfile + " " + linkfile;
321 result = std::system(ln.c_str());
322 ASSERT_EQ(result, 0);
323
324 wchar_t arg0[] = L"python";
325 wchar_t* arg1 = Py_DecodeLocale(linkfile.c_str(), nullptr);
326 wchar_t* wargv[] = {arg0, arg1};
327 PySys_SetArgv(1, wargv + 1);
328
329 PyObject* sys_path = PySys_GetObject("path");
330 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
331 char* abs_realpath = ::realpath(tmpdir1.path().c_str(), nullptr);
332 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, abs_realpath));
333
334 PyMem_RawFree(arg1);
335 std::free(abs_realpath);
336}
337
338TEST_F(SysModuleExtensionApiTest,
339 SetArgvWithRelativeSymlinkInsertsPathIntoSysPath) {
340 TempDirectory tmpdir;
341 std::string tmpfile = tmpdir.path() + "scriptfile.py";
342 std::string linkfile = tmpdir.path() + "scriptlink.py";
343 std::string touch = "touch " + tmpfile;
344 int result = std::system(touch.c_str());
345 ASSERT_EQ(result, 0);
346 std::string ln = "ln -s ./scriptfile.py " + linkfile;
347 result = std::system(ln.c_str());
348 ASSERT_EQ(result, 0);
349
350 wchar_t arg0[] = L"python";
351 wchar_t* arg1 = Py_DecodeLocale(linkfile.c_str(), nullptr);
352 wchar_t* wargv[] = {arg0, arg1};
353 PySys_SetArgv(1, wargv + 1);
354
355 PyObject* sys_path = PySys_GetObject("path");
356 PyObject* sys_path0 = PyList_GetItem(sys_path, 0);
357 char* abs_realpath = ::realpath(tmpdir.path().c_str(), nullptr);
358 EXPECT_TRUE(isUnicodeEqualsCStr(sys_path0, abs_realpath));
359
360 PyMem_RawFree(arg1);
361 std::free(abs_realpath);
362}
363
364} // namespace testing
365} // namespace py