test_file.cpp 7.8 KB
Newer Older
1
// Copyright 2017-2021, Schlumberger
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "test_all.h"
#include "test_utils.h"
#include "../exception.h"
#include "../iocontext.h"
#include "../impl/file.h"
#include "../impl/file_sd.h"
21
#include "../impl/file_smallcache.h"
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include "../impl/environment.h"

#include <iostream>
#include <memory>

// For fork/exec
#ifdef _WIN32
#else
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#endif
#include <stdio.h>

using namespace OpenZGY;
using namespace OpenZGY::Errors;
using namespace InternalZGY;
using Test_Utils::LocalFileAutoDelete;
using Test_Utils::CloudFileAutoDelete;

namespace {
#if 0
}
#endif

void deleteFile(const std::shared_ptr<FileADT>& file, const std::string& filename)
{
  if (filename.substr(0,5) == "sd://") {
    // TODO-Low: Use some kind of factory here.
#ifdef HAVE_SD
    auto fd_sd = dynamic_cast<InternalZGY::FileUtilsSeismicStore*>(file.get());
    if (!fd_sd)
      throw std::runtime_error("Need a seismic store file here.");
    fd_sd->deleteFile(filename, /*missing_ok=*/true);
#endif
  }
  else {
#ifdef _WIN32
    if (_unlink(filename.c_str()) < 0 && errno != ENOENT)
      throw std::runtime_error("Cannot delete file");
#else
    if (::unlink(filename.c_str()) < 0 && errno != ENOENT)
      throw std::runtime_error("Cannot delete file");
#endif
  }
}

void
run_helloworld(const std::string& fn, const std::function<std::shared_ptr<FileADT>(const std::string&, OpenMode)>& alloc)
{
  std::shared_ptr<FileADT> file;
  file = alloc(fn, OpenMode::Truncate);
  file->xx_write("Hello, world, what else can I say?\n", 0, 35);
  file->xx_close();
  file.reset();

  file = alloc(fn, OpenMode::ReadOnly);
  char data[100]{0};
  file->xx_read(data, 7, 5);
  TEST_CHECK(std::string(data) == std::string("world"));
82
  auto sink = [&data](ReadRequest::data_t ptr, std::int64_t size) {
83
                memcpy(data, ptr.get(), size);
84
85
86
87
              };
  ReadList requests{ ReadRequest(14, 9, sink) };
  file->xx_readv(requests);
  TEST_CHECK(std::string(data) == std::string("what else"));
88
89
90
91
92
93
94
95
96
97
98
99

  // Also test a wrapper to cache the first 16(!) bytes
  file.reset(new FileWithSmallCache(file, 16));
  std::fill(&data[0], &data[100], 0);
  file->xx_read(data, 7, 5);
  TEST_CHECK(std::string(data) == std::string("world"));
  std::fill(&data[0], &data[100], 0);
  file->xx_read(data, 1, 4); // will be cached
  TEST_CHECK(std::string(data) == std::string("ello"));
  std::fill(&data[0], &data[100], 0);
  file->xx_readv(requests); // not cached, crosses end of the cache
  TEST_CHECK(std::string(data) == std::string("what else"));
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  file->xx_close();
  file.reset();

  file = alloc(fn, OpenMode::Closed);
  deleteFile(file, fn);
  file.reset();

  // Attempt to delete non-existing file is not an error.
  file = alloc(fn, OpenMode::Closed);
  deleteFile(file, fn);
  file.reset();

  // Attempt to open a non-existing file is an error.
  try {
    file = alloc(fn, OpenMode::ReadOnly);
    TEST_CHECK(false && "Expected an exception about non-existing file.");
  }
  catch (const ZgyIoError& ex) {
    TEST_CHECK(ex.what() && std::string(ex.what()).find("No such file") != std::string::npos);
  }
  catch (const ZgyInternalError& ex) {
    // TODO-Low: A specific cloud-related error.
    // And/or a specific "file not found" error.
    TEST_CHECK(ex.what() && std::string(ex.what()).find("does not exist") != std::string::npos);
  }
}

void
test_localfilefactory()
{
  LocalFileAutoDelete lad("testfile.zgy");
  run_helloworld(lad.name(),
                 [](const std::string& name, OpenMode mode) {
                   return FileFactory::instance().create(
                       name, mode, nullptr);
                 });
}

Paal Kvamme's avatar
Paal Kvamme committed
138
#ifdef HAVE_SD
139
140
141
void
test_sdfilefactory()
{
142
  CloudFileAutoDelete cad("ozcpp-testfile.txt", Test_Utils::default_sd_context());
143
  run_helloworld(cad.name(),
144
                 [](const std::string& name, OpenMode mode) {
145
                   return FileFactory::instance().create(
146
                       name, mode, Test_Utils::default_sd_context());
147
148
149
                 });
  cad.disarm();
}
150
151
152
153
154
155
156
157
158

/**
 * This is primarily to verify that CloudFileAutoDelete works.
 */
void
test_sdfiledelete()
{
  std::string filename;
  {
159
    CloudFileAutoDelete cad("ozcpp-testdelete.txt", Test_Utils::default_sd_context());
160
161
    filename = cad.name();
    std::shared_ptr<FileADT> file = FileFactory::instance().create
162
      (filename, OpenMode::Truncate, Test_Utils::default_sd_context());
163
164
165
166
167
168
169
170
    file->xx_write("Hello, world, I will be auto-deleted\n", 0, 37);
    file->xx_close();
    file.reset();
  }

  // cad has gone out of scope, so the file ought to have been deleted.
  try {
    std::shared_ptr<FileADT> file = FileFactory::instance().create
171
      (filename, OpenMode::ReadOnly, Test_Utils::default_sd_context());
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
    TEST_CHECK(false && "Expected an exception about non-existing file.");
  }
  catch (const ZgyIoError& ex) {
    if (verbose())
      std::cout << "Got expected I/O exception " << ex.what() << std::endl;
    TEST_CHECK(ex.what() && std::string(ex.what()).find("No such file") != std::string::npos);
  }
  catch (const ZgyInternalError& ex) {
    if (verbose())
      std::cout << "Got expected internal exception " << ex.what() << std::endl;
    // TODO-Low: A specific cloud-related error.
    // And/or a specific "file not found" error.
    TEST_CHECK(ex.what() && std::string(ex.what()).find("does not exist") != std::string::npos);
  }
}
Paal Kvamme's avatar
Paal Kvamme committed
187
#endif
188
189
190
191
192
193
194
195

/**
 * Python one-liner to show the expiry time of the OPENZGY_TOKEN
 * if one is present. No error handling whatsoever. may be useful to
 * debug problems in the build server. Note that this needs to be caller
 * before any test that will access the seismic store, so it should be
 * registered as "aaa.sdtoken" since tests are run alphabetically.
 *
Paal Kvamme's avatar
Paal Kvamme committed
196
 * It would of course be better to do this in C++ but I don't want to have
197
198
199
200
201
202
203
204
205
206
207
 * base64 and jsoncpp dependencies just because of this.
 */
void
test_sdtoken()
{
  if (!Environment::getStringEnv("OPENZGY_TOKEN").empty()) {
    std::cerr << std::flush;
    std::cout << std::flush;
    fflush(stderr);
    fflush(stdout);
#ifdef _WIN32
Paal Kvamme's avatar
Paal Kvamme committed
208
    // I could probably have used system() on Linux as well, but who cares?
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
    system("python -c \"import base64, json, os, datetime; print('\\nToken ending with', os.getenv('OPENZGY_TOKEN')[-5:], datetime.datetime.fromtimestamp(json.loads(base64.urlsafe_b64decode(os.getenv('OPENZGY_TOKEN').split('.')[1] + '====').decode())['exp']).strftime('expires %d/%m/%y %H:%M'))\"");
#else
    auto pid = fork();
    switch (pid) {
    case 0:
      execlp("python3", "python3", "-c", "import base64,json,os,datetime; print('\\nToken ending with', os.getenv('OPENZGY_TOKEN')[-5:],datetime.datetime.fromtimestamp(json.loads(base64.urlsafe_b64decode(os.getenv('OPENZGY_TOKEN').split('.')[1]+'====').decode())['exp']).strftime('expires %d/%m/%y %H:%M'))", (char*)0);
      _exit(0);
      break;
    case -1:
      break;
    default:
      waitpid(pid, nullptr, 0);
      break;
    }
#endif
  }
}

} // namespace for tests

namespace {
  class Register
  {
  public:
    Register()
    {
      register_test("aaa.sdtoken",             test_sdtoken);
      register_test("file.localfilefactory",   test_localfilefactory);
#ifdef HAVE_SD
      register_test("file.sdfilefactory",      test_sdfilefactory);
239
      register_test("file.sdfiledelete",       test_sdfiledelete);
240
241
242
243
#endif
    }
  } dummy;
} // namespace for registration