xref: /aosp_15_r20/external/tensorflow/tensorflow/core/platform/env_test.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/core/platform/env.h"
17 
18 #include <sys/stat.h>
19 
20 #include "tensorflow/core/framework/graph.pb.h"
21 #include "tensorflow/core/framework/node_def.pb.h"
22 #include "tensorflow/core/lib/core/status_test_util.h"
23 #include "tensorflow/core/platform/cord.h"
24 #include "tensorflow/core/platform/null_file_system.h"
25 #include "tensorflow/core/platform/path.h"
26 #include "tensorflow/core/platform/protobuf.h"
27 #include "tensorflow/core/platform/str_util.h"
28 #include "tensorflow/core/platform/strcat.h"
29 #include "tensorflow/core/platform/stringpiece.h"
30 #include "tensorflow/core/platform/test.h"
31 
32 namespace tensorflow {
33 
34 namespace {
35 
CreateTestFile(Env * env,const string & filename,int length)36 string CreateTestFile(Env* env, const string& filename, int length) {
37   string input(length, 0);
38   for (int i = 0; i < length; i++) input[i] = i;
39   TF_EXPECT_OK(WriteStringToFile(env, filename, input));
40   return input;
41 }
42 
CreateTestProto()43 GraphDef CreateTestProto() {
44   GraphDef g;
45   NodeDef* node = g.add_node();
46   node->set_name("name1");
47   node->set_op("op1");
48   node = g.add_node();
49   node->set_name("name2");
50   node->set_op("op2");
51   return g;
52 }
53 
ExpectHasSubstr(StringPiece s,StringPiece expected)54 static void ExpectHasSubstr(StringPiece s, StringPiece expected) {
55   EXPECT_TRUE(absl::StrContains(s, expected))
56       << "'" << s << "' does not contain '" << expected << "'";
57 }
58 
59 }  // namespace
60 
BaseDir()61 string BaseDir() { return io::JoinPath(testing::TmpDir(), "base_dir"); }
62 
63 class DefaultEnvTest : public ::testing::Test {
64  protected:
SetUp()65   void SetUp() override { TF_CHECK_OK(env_->CreateDir(BaseDir())); }
66 
TearDown()67   void TearDown() override {
68     int64_t undeleted_files, undeleted_dirs;
69     TF_CHECK_OK(
70         env_->DeleteRecursively(BaseDir(), &undeleted_files, &undeleted_dirs));
71   }
72 
73   Env* env_ = Env::Default();
74 };
75 
TEST_F(DefaultEnvTest,IncompleteReadOutOfRange)76 TEST_F(DefaultEnvTest, IncompleteReadOutOfRange) {
77   const string filename = io::JoinPath(BaseDir(), "out_of_range");
78   const string input = CreateTestFile(env_, filename, 2);
79   std::unique_ptr<RandomAccessFile> f;
80   TF_EXPECT_OK(env_->NewRandomAccessFile(filename, &f));
81 
82   // Reading past EOF should give an OUT_OF_RANGE error
83   StringPiece result;
84   char scratch[3];
85   EXPECT_EQ(error::OUT_OF_RANGE, f->Read(0, 3, &result, scratch).code());
86   EXPECT_EQ(input, result);
87 
88   // Exact read to EOF works.
89   TF_EXPECT_OK(f->Read(0, 2, &result, scratch));
90   EXPECT_EQ(input, result);
91 }
92 
TEST_F(DefaultEnvTest,ReadFileToString)93 TEST_F(DefaultEnvTest, ReadFileToString) {
94   for (const int length : {0, 1, 1212, 2553, 4928, 8196, 9000, (1 << 20) - 1,
95                            1 << 20, (1 << 20) + 1, (256 << 20) + 100}) {
96     const string filename =
97         io::JoinPath(BaseDir(), "bar", "..", strings::StrCat("file", length));
98 
99     // Write a file with the given length
100     const string input = CreateTestFile(env_, filename, length);
101 
102     // Read the file back and check equality
103     string output;
104     TF_EXPECT_OK(ReadFileToString(env_, filename, &output));
105     EXPECT_EQ(length, output.size());
106     EXPECT_EQ(input, output);
107 
108     // Obtain stats.
109     FileStatistics stat;
110     TF_EXPECT_OK(env_->Stat(filename, &stat));
111     EXPECT_EQ(length, stat.length);
112     EXPECT_FALSE(stat.is_directory);
113   }
114 }
115 
TEST_F(DefaultEnvTest,ReadWriteBinaryProto)116 TEST_F(DefaultEnvTest, ReadWriteBinaryProto) {
117   const GraphDef proto = CreateTestProto();
118   const string filename = strings::StrCat(BaseDir(), "binary_proto");
119 
120   // Write the binary proto
121   TF_EXPECT_OK(WriteBinaryProto(env_, filename, proto));
122 
123   // Read the binary proto back in and make sure it's the same.
124   GraphDef result;
125   TF_EXPECT_OK(ReadBinaryProto(env_, filename, &result));
126   EXPECT_EQ(result.DebugString(), proto.DebugString());
127 
128   // Reading as text or binary proto should also work.
129   GraphDef result2;
130   TF_EXPECT_OK(ReadTextOrBinaryProto(env_, filename, &result2));
131   EXPECT_EQ(result2.DebugString(), proto.DebugString());
132 }
133 
TEST_F(DefaultEnvTest,ReadWriteTextProto)134 TEST_F(DefaultEnvTest, ReadWriteTextProto) {
135   const GraphDef proto = CreateTestProto();
136   const string filename = strings::StrCat(BaseDir(), "text_proto");
137 
138   // Write the text proto
139   string as_text;
140   EXPECT_TRUE(protobuf::TextFormat::PrintToString(proto, &as_text));
141   TF_EXPECT_OK(WriteStringToFile(env_, filename, as_text));
142 
143   // Read the text proto back in and make sure it's the same.
144   GraphDef result;
145   TF_EXPECT_OK(ReadTextProto(env_, filename, &result));
146   EXPECT_EQ(result.DebugString(), proto.DebugString());
147 
148   // Reading as text or binary proto should also work.
149   GraphDef result2;
150   TF_EXPECT_OK(ReadTextOrBinaryProto(env_, filename, &result2));
151   EXPECT_EQ(result2.DebugString(), proto.DebugString());
152 }
153 
TEST_F(DefaultEnvTest,FileToReadonlyMemoryRegion)154 TEST_F(DefaultEnvTest, FileToReadonlyMemoryRegion) {
155   for (const int length : {1, 1212, 2553, 4928, 8196, 9000, (1 << 20) - 1,
156                            1 << 20, (1 << 20) + 1}) {
157     const string filename =
158         io::JoinPath(BaseDir(), strings::StrCat("file", length));
159 
160     // Write a file with the given length
161     const string input = CreateTestFile(env_, filename, length);
162 
163     // Create the region.
164     std::unique_ptr<ReadOnlyMemoryRegion> region;
165     TF_EXPECT_OK(env_->NewReadOnlyMemoryRegionFromFile(filename, &region));
166     ASSERT_NE(region, nullptr);
167     EXPECT_EQ(length, region->length());
168     EXPECT_EQ(input, string(reinterpret_cast<const char*>(region->data()),
169                             region->length()));
170     FileStatistics stat;
171     TF_EXPECT_OK(env_->Stat(filename, &stat));
172     EXPECT_EQ(length, stat.length);
173     EXPECT_FALSE(stat.is_directory);
174   }
175 }
176 
TEST_F(DefaultEnvTest,DeleteRecursively)177 TEST_F(DefaultEnvTest, DeleteRecursively) {
178   // Build a directory structure rooted at root_dir.
179   // root_dir -> dirs: child_dir1, child_dir2; files: root_file1, root_file2
180   // child_dir1 -> files: child1_file1
181   // child_dir2 -> empty
182   const string parent_dir = io::JoinPath(BaseDir(), "root_dir");
183   const string child_dir1 = io::JoinPath(parent_dir, "child_dir1");
184   const string child_dir2 = io::JoinPath(parent_dir, "child_dir2");
185   TF_EXPECT_OK(env_->CreateDir(parent_dir));
186   const string root_file1 = io::JoinPath(parent_dir, "root_file1");
187   const string root_file2 = io::JoinPath(parent_dir, "root_file2");
188   const string root_file3 = io::JoinPath(parent_dir, ".root_file3");
189   CreateTestFile(env_, root_file1, 100);
190   CreateTestFile(env_, root_file2, 100);
191   CreateTestFile(env_, root_file3, 100);
192   TF_EXPECT_OK(env_->CreateDir(child_dir1));
193   const string child1_file1 = io::JoinPath(child_dir1, "child1_file1");
194   CreateTestFile(env_, child1_file1, 100);
195   TF_EXPECT_OK(env_->CreateDir(child_dir2));
196 
197   int64_t undeleted_files, undeleted_dirs;
198   TF_EXPECT_OK(
199       env_->DeleteRecursively(parent_dir, &undeleted_files, &undeleted_dirs));
200   EXPECT_EQ(0, undeleted_files);
201   EXPECT_EQ(0, undeleted_dirs);
202   EXPECT_EQ(error::Code::NOT_FOUND, env_->FileExists(root_file1).code());
203   EXPECT_EQ(error::Code::NOT_FOUND, env_->FileExists(root_file2).code());
204   EXPECT_EQ(error::Code::NOT_FOUND, env_->FileExists(root_file3).code());
205   EXPECT_EQ(error::Code::NOT_FOUND, env_->FileExists(child1_file1).code());
206 }
207 
TEST_F(DefaultEnvTest,DeleteRecursivelyFail)208 TEST_F(DefaultEnvTest, DeleteRecursivelyFail) {
209   // Try to delete a non-existent directory.
210   const string parent_dir = io::JoinPath(BaseDir(), "root_dir");
211 
212   int64_t undeleted_files, undeleted_dirs;
213   Status s =
214       env_->DeleteRecursively(parent_dir, &undeleted_files, &undeleted_dirs);
215   EXPECT_EQ(error::Code::NOT_FOUND, s.code());
216   EXPECT_EQ(0, undeleted_files);
217   EXPECT_EQ(1, undeleted_dirs);
218 }
219 
TEST_F(DefaultEnvTest,RecursivelyCreateDir)220 TEST_F(DefaultEnvTest, RecursivelyCreateDir) {
221   const string create_path = io::JoinPath(BaseDir(), "a", "b", "c", "d");
222   TF_CHECK_OK(env_->RecursivelyCreateDir(create_path));
223   TF_CHECK_OK(env_->RecursivelyCreateDir(create_path));  // repeat creation.
224   TF_EXPECT_OK(env_->FileExists(create_path));
225 }
226 
TEST_F(DefaultEnvTest,RecursivelyCreateDirEmpty)227 TEST_F(DefaultEnvTest, RecursivelyCreateDirEmpty) {
228   TF_CHECK_OK(env_->RecursivelyCreateDir(""));
229 }
230 
TEST_F(DefaultEnvTest,RecursivelyCreateDirSubdirsExist)231 TEST_F(DefaultEnvTest, RecursivelyCreateDirSubdirsExist) {
232   // First create a/b.
233   const string subdir_path = io::JoinPath(BaseDir(), "a", "b");
234   TF_CHECK_OK(env_->CreateDir(io::JoinPath(BaseDir(), "a")));
235   TF_CHECK_OK(env_->CreateDir(subdir_path));
236   TF_EXPECT_OK(env_->FileExists(subdir_path));
237 
238   // Now try to recursively create a/b/c/d/
239   const string create_path = io::JoinPath(BaseDir(), "a", "b", "c", "d");
240   TF_CHECK_OK(env_->RecursivelyCreateDir(create_path));
241   TF_CHECK_OK(env_->RecursivelyCreateDir(create_path));  // repeat creation.
242   TF_EXPECT_OK(env_->FileExists(create_path));
243   TF_EXPECT_OK(env_->FileExists(io::JoinPath(BaseDir(), "a", "b", "c")));
244 }
245 
TEST_F(DefaultEnvTest,LocalFileSystem)246 TEST_F(DefaultEnvTest, LocalFileSystem) {
247   // Test filename with file:// syntax.
248   int expected_num_files = 0;
249   std::vector<string> matching_paths;
250   for (const int length : {0, 1, 1212, 2553, 4928, 8196, 9000, (1 << 20) - 1,
251                            1 << 20, (1 << 20) + 1}) {
252     string filename = io::JoinPath(BaseDir(), strings::StrCat("len", length));
253 
254     filename = strings::StrCat("file://", filename);
255 
256     // Write a file with the given length
257     const string input = CreateTestFile(env_, filename, length);
258     ++expected_num_files;
259 
260     // Ensure that GetMatchingPaths works as intended.
261     TF_EXPECT_OK(env_->GetMatchingPaths(
262         // Try it with the "file://" URI scheme.
263         strings::StrCat("file://", io::JoinPath(BaseDir(), "l*")),
264         &matching_paths));
265     EXPECT_EQ(expected_num_files, matching_paths.size());
266     TF_EXPECT_OK(env_->GetMatchingPaths(
267         // Try it without any URI scheme.
268         io::JoinPath(BaseDir(), "l*"), &matching_paths));
269     EXPECT_EQ(expected_num_files, matching_paths.size());
270 
271     // Read the file back and check equality
272     string output;
273     TF_EXPECT_OK(ReadFileToString(env_, filename, &output));
274     EXPECT_EQ(length, output.size());
275     EXPECT_EQ(input, output);
276 
277     FileStatistics stat;
278     TF_EXPECT_OK(env_->Stat(filename, &stat));
279     EXPECT_EQ(length, stat.length);
280     EXPECT_FALSE(stat.is_directory);
281   }
282 }
283 
TEST_F(DefaultEnvTest,SleepForMicroseconds)284 TEST_F(DefaultEnvTest, SleepForMicroseconds) {
285   const int64_t start = env_->NowMicros();
286   const int64_t sleep_time = 1e6 + 5e5;
287   env_->SleepForMicroseconds(sleep_time);
288   const int64_t delta = env_->NowMicros() - start;
289 
290   // Subtract 200 from the sleep_time for this check because NowMicros can
291   // sometimes give slightly inconsistent values between the start and the
292   // finish (e.g. because the two calls run on different CPUs).
293   EXPECT_GE(delta, sleep_time - 200);
294 }
295 
296 class TmpDirFileSystem : public NullFileSystem {
297  public:
298   TF_USE_FILESYSTEM_METHODS_WITH_NO_TRANSACTION_SUPPORT;
299 
FileExists(const string & dir,TransactionToken * token)300   Status FileExists(const string& dir, TransactionToken* token) override {
301     StringPiece scheme, host, path;
302     io::ParseURI(dir, &scheme, &host, &path);
303     if (path.empty()) return errors::NotFound(dir, " not found");
304     // The special "flushed" file exists only if the filesystem's caches have
305     // been flushed.
306     if (path == "/flushed") {
307       if (flushed_) {
308         return OkStatus();
309       } else {
310         return errors::NotFound("FlushCaches() not called yet");
311       }
312     }
313     return Env::Default()->FileExists(io::JoinPath(BaseDir(), path));
314   }
315 
CreateDir(const string & dir,TransactionToken * token)316   Status CreateDir(const string& dir, TransactionToken* token) override {
317     StringPiece scheme, host, path;
318     io::ParseURI(dir, &scheme, &host, &path);
319     if (scheme != "tmpdirfs") {
320       return errors::FailedPrecondition("scheme must be tmpdirfs");
321     }
322     if (host != "testhost") {
323       return errors::FailedPrecondition("host must be testhost");
324     }
325     Status status = Env::Default()->CreateDir(io::JoinPath(BaseDir(), path));
326     if (status.ok()) {
327       // Record that we have created this directory so `IsDirectory` works.
328       created_directories_.push_back(std::string(path));
329     }
330     return status;
331   }
332 
IsDirectory(const string & dir,TransactionToken * token)333   Status IsDirectory(const string& dir, TransactionToken* token) override {
334     StringPiece scheme, host, path;
335     io::ParseURI(dir, &scheme, &host, &path);
336     for (const auto& existing_dir : created_directories_)
337       if (existing_dir == path) return OkStatus();
338     return errors::NotFound(dir, " not found");
339   }
340 
FlushCaches(TransactionToken * token)341   void FlushCaches(TransactionToken* token) override { flushed_ = true; }
342 
343  private:
344   bool flushed_ = false;
345   std::vector<std::string> created_directories_ = {"/"};
346 };
347 
348 REGISTER_FILE_SYSTEM("tmpdirfs", TmpDirFileSystem);
349 
TEST_F(DefaultEnvTest,FlushFileSystemCaches)350 TEST_F(DefaultEnvTest, FlushFileSystemCaches) {
351   Env* env = Env::Default();
352   const string flushed =
353       strings::StrCat("tmpdirfs://", io::JoinPath("testhost", "flushed"));
354   EXPECT_EQ(error::Code::NOT_FOUND, env->FileExists(flushed).code());
355   TF_EXPECT_OK(env->FlushFileSystemCaches());
356   TF_EXPECT_OK(env->FileExists(flushed));
357 }
358 
TEST_F(DefaultEnvTest,RecursivelyCreateDirWithUri)359 TEST_F(DefaultEnvTest, RecursivelyCreateDirWithUri) {
360   Env* env = Env::Default();
361   const string create_path = strings::StrCat(
362       "tmpdirfs://", io::JoinPath("testhost", "a", "b", "c", "d"));
363   EXPECT_EQ(error::Code::NOT_FOUND, env->FileExists(create_path).code());
364   TF_CHECK_OK(env->RecursivelyCreateDir(create_path));
365   TF_CHECK_OK(env->RecursivelyCreateDir(create_path));  // repeat creation.
366   TF_EXPECT_OK(env->FileExists(create_path));
367 }
368 
TEST_F(DefaultEnvTest,GetExecutablePath)369 TEST_F(DefaultEnvTest, GetExecutablePath) {
370   Env* env = Env::Default();
371   TF_EXPECT_OK(env->FileExists(env->GetExecutablePath()));
372 }
373 
TEST_F(DefaultEnvTest,LocalTempFilename)374 TEST_F(DefaultEnvTest, LocalTempFilename) {
375   Env* env = Env::Default();
376   string filename;
377   EXPECT_TRUE(env->LocalTempFilename(&filename));
378   EXPECT_FALSE(env->FileExists(filename).ok());
379 
380   // Write something to the temporary file.
381   std::unique_ptr<WritableFile> file_to_write;
382   TF_CHECK_OK(env->NewWritableFile(filename, &file_to_write));
383 #if defined(PLATFORM_GOOGLE)
384   TF_CHECK_OK(file_to_write->Append("Nu"));
385   TF_CHECK_OK(file_to_write->Append(absl::Cord("ll")));
386 #else
387   // TODO(ebrevdo): Remove this version.
388   TF_CHECK_OK(file_to_write->Append("Null"));
389 #endif
390   TF_CHECK_OK(file_to_write->Close());
391   TF_CHECK_OK(env->FileExists(filename));
392 
393   // Open the file in append mode, check that Tell() reports the appropriate
394   // offset.
395   std::unique_ptr<WritableFile> file_to_append;
396   TF_CHECK_OK(env->NewAppendableFile(filename, &file_to_append));
397   int64_t pos;
398   TF_CHECK_OK(file_to_append->Tell(&pos));
399   ASSERT_EQ(4, pos);
400 
401   // Read from the temporary file and check content.
402   std::unique_ptr<RandomAccessFile> file_to_read;
403   TF_CHECK_OK(env->NewRandomAccessFile(filename, &file_to_read));
404   StringPiece content;
405   char scratch[1024];
406   CHECK_EQ(
407       error::OUT_OF_RANGE,
408       file_to_read->Read(/*offset=*/0, /*n=*/1024, &content, scratch).code());
409   EXPECT_EQ("Null", content);
410 
411   // Delete the temporary file.
412   TF_CHECK_OK(env->DeleteFile(filename));
413   EXPECT_FALSE(env->FileExists(filename).ok());
414 }
415 
TEST_F(DefaultEnvTest,CreateUniqueFileName)416 TEST_F(DefaultEnvTest, CreateUniqueFileName) {
417   Env* env = Env::Default();
418 
419   string prefix = "tempfile-prefix-";
420   string suffix = ".tmp";
421   string filename = prefix;
422 
423   EXPECT_TRUE(env->CreateUniqueFileName(&filename, suffix));
424 
425   EXPECT_TRUE(absl::StartsWith(filename, prefix));
426   EXPECT_TRUE(str_util::EndsWith(filename, suffix));
427 }
428 
TEST_F(DefaultEnvTest,GetProcessId)429 TEST_F(DefaultEnvTest, GetProcessId) {
430   Env* env = Env::Default();
431   EXPECT_NE(env->GetProcessId(), 0);
432 }
433 
TEST_F(DefaultEnvTest,GetThreadInformation)434 TEST_F(DefaultEnvTest, GetThreadInformation) {
435   Env* env = Env::Default();
436   // TODO(fishx): Turn on this test for Apple.
437 #if !defined(__APPLE__)
438   EXPECT_NE(env->GetCurrentThreadId(), 0);
439 #endif
440   string thread_name;
441   bool res = env->GetCurrentThreadName(&thread_name);
442 #if defined(PLATFORM_WINDOWS) || defined(__ANDROID__)
443   EXPECT_FALSE(res);
444 #elif !defined(__APPLE__)
445   EXPECT_TRUE(res);
446   EXPECT_GT(thread_name.size(), 0);
447 #endif
448 }
449 
TEST_F(DefaultEnvTest,GetChildThreadInformation)450 TEST_F(DefaultEnvTest, GetChildThreadInformation) {
451   Env* env = Env::Default();
452   Thread* child_thread = env->StartThread({}, "tf_child_thread", [env]() {
453   // TODO(fishx): Turn on this test for Apple.
454 #if !defined(__APPLE__)
455     EXPECT_NE(env->GetCurrentThreadId(), 0);
456 #endif
457     string thread_name;
458     bool res = env->GetCurrentThreadName(&thread_name);
459     EXPECT_TRUE(res);
460     ExpectHasSubstr(thread_name, "tf_child_thread");
461   });
462   delete child_thread;
463 }
464 
465 }  // namespace tensorflow
466