1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 
25 #include "pxr/pxr.h"
26 #include "pxr/base/tf/setenv.h"
27 
28 #include "pxr/base/tf/errorMark.h"
29 #include "pxr/base/tf/getenv.h"
30 #include "pxr/base/tf/regTest.h"
31 #include "pxr/base/tf/stringUtils.h"
32 
33 #ifdef PXR_PYTHON_SUPPORT_ENABLED
34 #include "pxr/base/tf/pyUtils.h"
35 
36 #include <boost/python/handle.hpp>
37 #include <boost/python/extract.hpp>
38 #endif // PXR_PYTHON_SUPPORT_ENABLED
39 
40 #include <mutex>
41 #include <string>
42 #include <cstdio>
43 
44 using std::string;
45 
46 #ifdef PXR_PYTHON_SUPPORT_ENABLED
47 using namespace boost::python;
48 #endif // PXR_PYTHON_SUPPORT_ENABLED
49 
50 PXR_NAMESPACE_USING_DIRECTIVE
51 
52 static unsigned int
_CheckResultInEnv(const string & envName,const string & envVal)53 _CheckResultInEnv(const string & envName, const string & envVal)
54 {
55     string result = TfGetenv(envName);
56     if (result != envVal) {
57         printf("ERROR: Expected '%s', got '%s'.\n",
58                envVal.c_str(),
59                result.c_str());
60 
61         return 1;
62     }
63 
64     return 0;
65 }
66 
67 #ifdef PXR_PYTHON_SUPPORT_ENABLED
68 static unsigned int
_CheckResultInOsEnviron(const string & envName,const string & envVal)69 _CheckResultInOsEnviron(const string & envName, const string & envVal)
70 {
71     static std::once_flag once;
72     std::call_once(once, [](){
73         TfPyRunSimpleString("import os\n");
74     });
75 
76     TfPyLock lock;
77 
78     string cmd = TfStringPrintf("os.environ['%s']", envName.c_str());
79     handle<> wrappedResult = TfPyRunString(cmd, Py_eval_input);
80 
81     if (!wrappedResult) {
82         printf("ERROR: Python returned no result.\n");
83         return 1;
84     }
85 
86     extract<string> getString(wrappedResult.get());
87 
88     if (!getString.check()) {
89         printf("ERROR: Python returned non-string result.\n");
90         return 1;
91     }
92 
93     string result = getString();
94     if (result != envVal) {
95         printf("ERROR: Expected '%s', got '%s'.\n",
96                envVal.c_str(),
97                result.c_str());
98 
99         return 1;
100     }
101 
102     return 0;
103 }
104 
105 static unsigned int
_CheckResultNotInOsEnviron(const string & envName)106 _CheckResultNotInOsEnviron(const string & envName)
107 {
108     static std::once_flag once;
109     std::call_once(once, [](){
110         TfPyRunSimpleString("import os\n");
111     });
112 
113     TfPyLock lock;
114 
115     string cmd = TfStringPrintf("'%s' not in os.environ", envName.c_str());
116     handle<> wrappedResult = TfPyRunString(cmd, Py_eval_input);
117 
118     if (!wrappedResult) {
119         printf("ERROR: Python returned no result.\n");
120         return 1;
121     }
122 
123     extract<bool> getBool(wrappedResult.get());
124 
125     if (!getBool.check()) {
126         printf("ERROR: Python returned non-bool result.\n");
127         return 1;
128     }
129 
130     bool result = getBool();
131     if (!result) {
132         printf("ERROR: Expected key '%s' not appear in os.environ.\n",
133                envName.c_str());
134 
135         return 1;
136     }
137 
138     return 0;
139 }
140 
141 static unsigned int
_TestPySetenvNoInit()142 _TestPySetenvNoInit()
143 {
144     // Test that calling TfPySetenv causes an error.
145 
146     unsigned int numErrors = 0;
147 
148     const string envName = "PY_TEST_ENV_NAME";
149     const string envVal = "TestPySetenvNoInit";
150 
151     if (TfPyIsInitialized()) {
152         numErrors += 1;
153         printf("ERROR: Python should not yet be initialized.\n");
154         return numErrors;
155     }
156 
157     {
158         TfErrorMark m;
159         fprintf(stderr, "===== Expected Error =====\n");
160         bool didSet = TfPySetenv(envName, envVal);
161         fprintf(stderr, "=== End Expected Error ===\n");
162         if (didSet) {
163             numErrors += 1;
164             printf("ERROR: Calling TfPySetenv with uninitialized Python "
165                    "should return false.");
166         }
167 
168         if (m.IsClean()) {
169             numErrors += 1;
170             printf("ERROR: Calling TfPySetenv with uninitialized Python "
171                    "should produce an error.");
172         }
173         m.Clear();
174     }
175 
176     if (TfPyIsInitialized()) {
177         numErrors += 1;
178         printf("ERROR: Python should not yet be initialized.\n");
179         return numErrors;
180     }
181 
182     numErrors += _CheckResultInEnv(envName, "");
183 
184     return numErrors;
185 }
186 
187 static unsigned int
_TestPySetenvInit()188 _TestPySetenvInit()
189 {
190     // Initialize Python and verify that we can set/unset values and have them
191     // appear in os.environ.  Setting them into os.environ will also propagate
192     // them to the process environment.
193 
194     unsigned int numErrors = 0;
195 
196     const string envName = "PY_TEST_ENV_NAME";
197     const string envVal = "TestPySetenvInit";
198 
199     TfPyInitialize();
200 
201     TfPySetenv(envName, envVal);
202 
203     numErrors += _CheckResultInEnv(envName, envVal);
204     numErrors += _CheckResultInOsEnviron(envName, envVal);
205 
206     TfPyUnsetenv(envName);
207 
208     numErrors += _CheckResultInEnv(envName, "");
209     numErrors += _CheckResultNotInOsEnviron(envName);
210 
211     return numErrors;
212 }
213 
214 #endif // PXR_PYTHON_SUPPORT_ENABLED
215 
216 static unsigned int
_TestSetenvNoInit()217 _TestSetenvNoInit()
218 {
219     // Test that calling TfSetenv/TfUnsetenv without Python initialized still
220     // sets/unsets the value into the process environment.
221 
222     unsigned int numErrors = 0;
223 
224     const string envName = "TEST_ENV_NAME";
225     const string envVal = "TestSetenvNoInit";
226 
227 #ifdef PXR_PYTHON_SUPPORT_ENABLED
228     if (TfPyIsInitialized()) {
229         numErrors += 1;
230         printf("ERROR: Python should not yet be initialized.\n");
231         return numErrors;
232     }
233 #endif // PXR_PYTHON_SUPPORT_ENABLED
234 
235     {
236         TfErrorMark m;
237 
238         if (!TfSetenv(envName, envVal)) {
239             numErrors += 1;
240             printf("ERROR: Setenv failed\n");
241         }
242 
243         // Depend on the Tf error system to ouput any error messages.
244         size_t n = 0;
245         m.GetBegin(&n);
246         numErrors += n;
247 
248         m.Clear();
249     }
250 
251 #ifdef PXR_PYTHON_SUPPORT_ENABLED
252     if (TfPyIsInitialized()) {
253         numErrors += 1;
254         printf("ERROR: Python should not yet be initialized.\n");
255     }
256 #endif // PXR_PYTHON_SUPPORT_ENABLED
257 
258     numErrors += _CheckResultInEnv(envName, envVal);
259 
260     if (!TfUnsetenv(envName)) {
261         numErrors += 1;
262         printf("ERROR: Unsetenv failed\n");
263     }
264 
265     numErrors += _CheckResultInEnv(envName, "");
266 
267     return numErrors;
268 }
269 
270 static unsigned int
_TestSetenvInit()271 _TestSetenvInit()
272 {
273     // Test that TfSetenv/TfUnsetenv sets/unsets the value into both the
274     // process environment and os.environ.
275 
276     unsigned int numErrors = 0;
277 
278     const string envName = "TEST_ENV_NAME";
279     const string envVal = "TestSetenvInit";
280 
281 #ifdef PXR_PYTHON_SUPPORT_ENABLED
282     TfPyInitialize();
283 #endif // PXR_PYTHON_SUPPORT_ENABLED
284 
285     TfSetenv(envName, envVal);
286 
287     numErrors += _CheckResultInEnv(envName, envVal);
288 #ifdef PXR_PYTHON_SUPPORT_ENABLED
289     numErrors += _CheckResultInOsEnviron(envName, envVal);
290 #endif // PXR_PYTHON_SUPPORT_ENABLED
291 
292     TfUnsetenv(envName);
293 
294     numErrors += _CheckResultInEnv(envName, "");
295 #ifdef PXR_PYTHON_SUPPORT_ENABLED
296     numErrors += _CheckResultNotInOsEnviron(envName);
297 #endif // PXR_PYTHON_SUPPORT_ENABLED
298 
299     return numErrors;
300 }
301 
302 static bool
Test_TfSetenv(int argc,char ** argv)303 Test_TfSetenv(int argc, char **argv)
304 {
305     unsigned int numErrors = 0;
306 
307     numErrors += _TestSetenvNoInit();
308 #ifdef PXR_PYTHON_SUPPORT_ENABLED
309     numErrors += _TestPySetenvNoInit();
310 #endif // PXR_PYTHON_SUPPORT_ENABLED
311     numErrors += _TestSetenvInit();
312 #ifdef PXR_PYTHON_SUPPORT_ENABLED
313     numErrors += _TestPySetenvInit();
314 #endif // PXR_PYTHON_SUPPORT_ENABLED
315 
316     bool success = (numErrors == 0);
317 
318     // Print status
319     if (success) {
320         printf("\nTest SUCCEEDED\n");
321     } else {
322         printf("\nTest FAILED\n");
323     }
324 
325     return success;
326 }
327 
328 TF_ADD_REGTEST(TfSetenv);
329