1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <errno.h>
6#include <fcntl.h>
7#include <pthread.h>
8
9#include <set>
10#include <string>
11
12#include <gmock/gmock.h>
13#include <ppapi/c/pp_errors.h>
14#include <ppapi/c/pp_instance.h>
15
16#include "fake_ppapi/fake_messaging_interface.h"
17#include "fake_ppapi/fake_pepper_interface.h"
18#include "nacl_io/ioctl.h"
19#include "nacl_io/jsfs/js_fs.h"
20#include "nacl_io/jsfs/js_fs_node.h"
21#include "nacl_io/kernel_intercept.h"
22#include "nacl_io/kernel_proxy.h"
23#include "nacl_io/log.h"
24#include "nacl_io/osdirent.h"
25#include "nacl_io/osunistd.h"
26#include "sdk_util/auto_lock.h"
27#include "sdk_util/scoped_ref.h"
28#include "sdk_util/simple_lock.h"
29
30using namespace nacl_io;
31using namespace sdk_util;
32
33namespace {
34
35class JsFsForTesting : public JsFs {
36 public:
37  JsFsForTesting(PepperInterface* ppapi) {
38    FsInitArgs args;
39    args.ppapi = ppapi;
40    Error error = Init(args);
41    EXPECT_EQ(0, error);
42  }
43};
44
45class FakeMessagingInterfaceJsFs : public MessagingInterface {
46 public:
47  explicit FakeMessagingInterfaceJsFs(VarInterface* var_interface)
48      : var_interface_(var_interface), has_message_(false) {
49    pthread_cond_init(&cond_, NULL);
50  }
51
52  ~FakeMessagingInterfaceJsFs() { pthread_cond_destroy(&cond_); }
53
54  virtual void PostMessage(PP_Instance instance, PP_Var message) {
55    var_interface_->AddRef(message);
56
57    AUTO_LOCK(lock_);
58    ASSERT_FALSE(has_message_);
59
60    message_ = message;
61    has_message_ = true;
62    pthread_cond_signal(&cond_);
63  }
64
65  PP_Var WaitForMessage() {
66    AUTO_LOCK(lock_);
67    while (!has_message_) {
68      pthread_cond_wait(&cond_, lock_.mutex());
69    }
70
71    has_message_ = false;
72    return message_;
73  }
74
75 private:
76  VarInterface* var_interface_;
77  SimpleLock lock_;
78  pthread_cond_t cond_;
79  PP_Var message_;
80  bool has_message_;
81};
82
83class FakePepperInterfaceJsFs : public FakePepperInterface {
84 public:
85  FakePepperInterfaceJsFs() : messaging_interface_(GetVarInterface()) {}
86
87  virtual nacl_io::MessagingInterface* GetMessagingInterface() {
88    return &messaging_interface_;
89  }
90
91 private:
92  FakeMessagingInterfaceJsFs messaging_interface_;
93};
94
95class JsFsTest : public ::testing::Test {
96 public:
97  void SetUp() {
98    ASSERT_EQ(0, ki_push_state_for_testing());
99    ASSERT_EQ(0, ki_init_interface(&kp_, &ppapi_));
100    fs_.reset(new JsFsForTesting(&ppapi_));
101
102    js_thread_started_ = false;
103  }
104
105  void TearDown() {
106    if (js_thread_started_)
107      pthread_join(js_thread_, NULL);
108
109    for (RequestResponses::iterator it = request_responses_.begin(),
110                                    end = request_responses_.end();
111         it != end;
112         ++it) {
113      ppapi_.GetVarInterface()->Release(it->request);
114      ppapi_.GetVarInterface()->Release(it->response);
115    }
116
117
118    ki_uninit();
119  }
120
121  void StartJsThread() {
122    ASSERT_EQ(0, pthread_create(&js_thread_, NULL, JsThreadMainThunk, this));
123    js_thread_started_ = true;
124  }
125
126  static void* JsThreadMainThunk(void* arg) {
127    static_cast<JsFsTest*>(arg)->JsThreadMain();
128    return NULL;
129  }
130
131  PP_Var WaitForRequest() {
132    FakeMessagingInterfaceJsFs* messaging_if =
133        static_cast<FakeMessagingInterfaceJsFs*>(
134            ppapi_.GetMessagingInterface());
135    return messaging_if->WaitForMessage();
136  }
137
138  void JsThreadMain() {
139    for (RequestResponses::iterator it = request_responses_.begin(),
140                                    end = request_responses_.end();
141         it != end;
142         ++it) {
143      PP_Var request = WaitForRequest();
144      EXPECT_TRUE(VarsAreEqual(it->request, request))
145          << "Vars are not equal: " << VarToString(it->request)
146          << " != " << VarToString(request);
147      ppapi_.GetVarInterface()->Release(request);
148      EXPECT_EQ(0,
149                fs_->Filesystem_Ioctl(NACL_IOC_HANDLEMESSAGE, &it->response));
150      // Passed ownership of response_ to filesystem, so set this to undefined
151      // so it isn't double-released in TearDown().
152      it->response = PP_MakeUndefined();
153    }
154  }
155
156  void Expect(PP_Var request, PP_Var response) {
157    RequestResponse rr;
158    // Pass ownership of both vars from caller to callee.
159    rr.request = request;
160    rr.response = response;
161    request_responses_.push_back(rr);
162  }
163
164  bool CreateDict(PP_Var* out_var) {
165    *out_var = ppapi_.GetVarDictionaryInterface()->Create();
166    return out_var->type == PP_VARTYPE_DICTIONARY;
167  }
168
169  bool SetDictKeyValue(PP_Var* var, const char* key, int32_t value) {
170    return SetDictKeyValue(var, key, PP_MakeInt32(value));
171  }
172
173  bool SetDictKeyValue(PP_Var* var, const char* key, size_t value) {
174    return SetDictKeyValue(var, key, PP_MakeInt32(static_cast<size_t>(value)));
175  }
176
177  bool SetDictKeyValue(PP_Var* var, const char* key, int64_t value) {
178    VarArrayInterface* array_if = ppapi_.GetVarArrayInterface();
179    PP_Var value_var = array_if->Create();
180    return array_if->Set(value_var, 0, PP_MakeInt32(value >> 32)) &&
181           array_if->Set(value_var, 1, PP_MakeInt32(value & 0xffffffff)) &&
182           SetDictKeyValue(var, key, value_var);
183  }
184
185  bool SetDictKeyValue(PP_Var* var, const char* key, const char* value) {
186    VarInterface* var_if = ppapi_.GetVarInterface();
187    PP_Var value_var = var_if->VarFromUtf8(value, strlen(value));
188    return SetDictKeyValue(var, key, value_var);
189  }
190
191  bool SetDictKeyValue(PP_Var* var, const char* key, PP_Var value_var) {
192    VarDictionaryInterface* dict_if = ppapi_.GetVarDictionaryInterface();
193    VarInterface* var_if = ppapi_.GetVarInterface();
194    PP_Var key_var = var_if->VarFromUtf8(key, strlen(key));
195    PP_Bool result = dict_if->Set(*var, key_var, value_var);
196    var_if->Release(key_var);
197    var_if->Release(value_var);
198    return result == PP_TRUE;
199  }
200
201  bool CreateArray(PP_Var* out_var) {
202    *out_var = ppapi_.GetVarArrayInterface()->Create();
203    return out_var->type == PP_VARTYPE_ARRAY;
204  }
205
206  bool SetArrayValue(PP_Var* var, uint32_t i, int32_t value) {
207    return SetArrayValue(var, i, PP_MakeInt32(value));
208  }
209
210  bool SetArrayValue(PP_Var* var, uint32_t i, const char* value) {
211    VarInterface* var_if = ppapi_.GetVarInterface();
212    PP_Var value_var = var_if->VarFromUtf8(value, strlen(value));
213    return SetArrayValue(var, i, value_var);
214  }
215
216  bool SetArrayValue(PP_Var* var, uint32_t i, int64_t value) {
217    VarArrayInterface* array_if = ppapi_.GetVarArrayInterface();
218    PP_Var value_var = array_if->Create();
219    return array_if->Set(value_var, 0, PP_MakeInt32(value >> 32)) &&
220           array_if->Set(value_var, 1, PP_MakeInt32(value & 0xffffffff)) &&
221           SetArrayValue(var, i, value_var);
222  }
223
224  bool SetArrayValue(PP_Var* var, uint32_t i, PP_Var value_var) {
225    VarArrayInterface* array_if = ppapi_.GetVarArrayInterface();
226    VarInterface* var_if = ppapi_.GetVarInterface();
227    PP_Bool result = array_if->Set(*var, i, value_var);
228    var_if->Release(value_var);
229    return result == PP_TRUE;
230  }
231
232  std::string VarToString(PP_Var var) {
233    VarDictionaryInterface* dict_if = ppapi_.GetVarDictionaryInterface();
234    VarArrayInterface* array_if = ppapi_.GetVarArrayInterface();
235    VarInterface* var_if = ppapi_.GetVarInterface();
236    VarArrayBufferInterface* array_buffer_if =
237        ppapi_.GetVarArrayBufferInterface();
238
239    switch (var.type) {
240      case PP_VARTYPE_UNDEFINED:
241        return "undefined";
242      case PP_VARTYPE_NULL:
243        return "null";
244      case PP_VARTYPE_BOOL:
245        return var.value.as_bool ? "true" : "false";
246      case PP_VARTYPE_INT32: {
247        char buffer[32];
248        snprintf(buffer, 32, "%d", var.value.as_int);
249        return buffer;
250      }
251      case PP_VARTYPE_DOUBLE: {
252        char buffer[32];
253        snprintf(buffer, 32, "%g", var.value.as_double);
254        return buffer;
255      }
256      case PP_VARTYPE_STRING: {
257        uint32_t var_len;
258        const char* var_str = var_if->VarToUtf8(var, &var_len);
259        std::string result("\"");
260        result += std::string(var_str, var_len);
261        result += "\"";
262        return result;
263      }
264      case PP_VARTYPE_ARRAY: {
265        std::string result("[");
266        uint32_t var_len = array_if->GetLength(var);
267
268        for (uint32_t i = 0; i < var_len; ++i) {
269          PP_Var var_item = array_if->Get(var, i);
270          result += VarToString(var_item);
271          var_if->Release(var_item);
272          if (i != var_len - 1)
273            result += ", ";
274        }
275        result += "]";
276        return result;
277      }
278      case PP_VARTYPE_DICTIONARY: {
279        std::string result("{");
280        PP_Var var_keys = dict_if->GetKeys(var);
281        uint32_t var_len = array_if->GetLength(var_keys);
282
283        for (uint32_t i = 0; i < var_len; ++i) {
284          PP_Var key = array_if->Get(var_keys, i);
285          result += VarToString(key);
286          result += ": ";
287          PP_Var var_value = dict_if->Get(var, key);
288          result += VarToString(var_value);
289          var_if->Release(key);
290          var_if->Release(var_value);
291          if (i != var_len - 1)
292            result += ", ";
293        }
294        result += "}";
295        var_if->Release(var_keys);
296        return result;
297      }
298      case PP_VARTYPE_ARRAY_BUFFER: {
299        uint32_t var_len;
300        if (!array_buffer_if->ByteLength(var, &var_len)) {
301          LOG_ERROR("Unable to get byte length of var.");
302          return "undefined";
303        }
304
305        std::string result("new Uint8Array([");
306
307        void* var_ptr = array_buffer_if->Map(var);
308        for (uint32_t i = 0; i < var_len; ++i) {
309          char buffer[8];
310          snprintf(buffer, 8, "%d", static_cast<uint8_t*>(var_ptr)[i]);
311          result += buffer;
312          if (i != var_len - 1)
313            result += ", ";
314        }
315        result += "])";
316        array_buffer_if->Unmap(var);
317        return result;
318      }
319
320      default:
321        ADD_FAILURE() << "Unexpected var type: " << var.type;
322        return "undefined";
323    }
324  }
325
326  bool VarsAreEqual(PP_Var expected, PP_Var var) {
327    if (expected.type != var.type)
328      return false;
329
330    VarDictionaryInterface* dict_if = ppapi_.GetVarDictionaryInterface();
331    VarArrayInterface* array_if = ppapi_.GetVarArrayInterface();
332    VarInterface* var_if = ppapi_.GetVarInterface();
333    VarArrayBufferInterface* array_buffer_if =
334        ppapi_.GetVarArrayBufferInterface();
335
336    switch (var.type) {
337      case PP_VARTYPE_UNDEFINED:
338      case PP_VARTYPE_NULL:
339        return true;
340      case PP_VARTYPE_BOOL:
341        return expected.value.as_bool == var.value.as_bool;
342      case PP_VARTYPE_INT32:
343        return expected.value.as_int == var.value.as_int;
344      case PP_VARTYPE_DOUBLE:
345        return expected.value.as_double == var.value.as_double;
346      case PP_VARTYPE_STRING: {
347        uint32_t var_len;
348        uint32_t expected_len;
349        const char* var_str = var_if->VarToUtf8(var, &var_len);
350        const char* expected_str = var_if->VarToUtf8(expected, &expected_len);
351
352        if (expected_len != var_len)
353          return false;
354
355        return memcmp(expected_str, var_str, var_len) == 0;
356      }
357      case PP_VARTYPE_ARRAY: {
358        uint32_t var_len = array_if->GetLength(var);
359        uint32_t expected_len = array_if->GetLength(expected);
360
361        if (expected_len != var_len)
362          return false;
363
364        for (uint32_t i = 0; i < var_len; ++i) {
365          PP_Var var_item = array_if->Get(var, i);
366          PP_Var expected_item = array_if->Get(expected, i);
367          bool equal = VarsAreEqual(expected_item, var_item);
368          var_if->Release(var_item);
369          var_if->Release(expected_item);
370
371          if (!equal)
372            return false;
373        }
374
375        return true;
376      }
377      case PP_VARTYPE_DICTIONARY: {
378        PP_Var var_keys = dict_if->GetKeys(var);
379        PP_Var expected_keys = dict_if->GetKeys(expected);
380
381        uint32_t var_len = array_if->GetLength(var_keys);
382        uint32_t expected_len = array_if->GetLength(expected_keys);
383
384        bool result = true;
385
386        if (expected_len == var_len) {
387          for (uint32_t i = 0; i < var_len; ++i) {
388            PP_Var key = array_if->Get(var_keys, i);
389            PP_Var var_value = dict_if->Get(var, key);
390            PP_Var expected_value = dict_if->Get(expected, key);
391            bool equal = VarsAreEqual(expected_value, var_value);
392            var_if->Release(key);
393            var_if->Release(var_value);
394            var_if->Release(expected_value);
395
396            if (!equal) {
397              result = false;
398              break;
399            }
400          }
401        } else {
402          result = false;
403        }
404
405        var_if->Release(var_keys);
406        var_if->Release(expected_keys);
407        return result;
408      }
409      case PP_VARTYPE_ARRAY_BUFFER: {
410        uint32_t var_len;
411        if (!array_buffer_if->ByteLength(var, &var_len))
412          return false;
413
414        uint32_t expected_len;
415        if (!array_buffer_if->ByteLength(expected, &expected_len))
416          return false;
417
418        if (expected_len != var_len)
419          return false;
420
421        void* var_ptr = array_buffer_if->Map(var);
422        void* expected_ptr = array_buffer_if->Map(expected);
423        bool equal = memcmp(var_ptr, expected_ptr, var_len) == 0;
424        array_buffer_if->Unmap(var);
425        array_buffer_if->Unmap(expected);
426
427        return equal;
428      }
429
430      default:
431        ADD_FAILURE() << "Unexpected var type: " << var.type;
432        return false;
433    }
434  }
435
436  PP_Var CreateDummyArrayBuffer(uint32_t length) {
437    VarArrayBufferInterface* array_buffer_if =
438        ppapi_.GetVarArrayBufferInterface();
439    PP_Var var = array_buffer_if->Create(length);
440    uint8_t* data = static_cast<uint8_t*>(array_buffer_if->Map(var));
441    FillDummyBuffer(data, length);
442    array_buffer_if->Unmap(var);
443    return var;
444  }
445
446  void FillDummyBuffer(uint8_t* buf, size_t buf_len) {
447    for (uint32_t i = 0; i < buf_len; ++i) {
448      buf[i] = i & 255;
449    }
450  }
451
452  bool EqualsDummyArrayBuffer(uint8_t* buf, size_t buf_len) {
453    for (uint32_t i = 0; i < buf_len; ++i) {
454      if (buf[i] != (i & 255)) {
455        LOG_ERROR("Byte %d of ArrayBuffer doesn't match: %d != %d.",
456                  i,
457                  buf[i],
458                  i & 255);
459        return false;
460      }
461    }
462    return true;
463  }
464
465 protected:
466  FakePepperInterfaceJsFs ppapi_;
467  ScopedRef<JsFsForTesting> fs_;
468  KernelProxy kp_;
469  pthread_t js_thread_;
470  bool js_thread_started_;
471
472  struct RequestResponse {
473    PP_Var request;
474    PP_Var response;
475  };
476
477  typedef std::vector<RequestResponse> RequestResponses;
478  RequestResponses request_responses_;
479};
480
481class JsFsNodeTest : public JsFsTest {
482 public:
483  static const int fd;
484
485  virtual void SetUp() {
486    JsFsTest::SetUp();
487
488    PP_Var expected;
489    ASSERT_EQ(true, CreateDict(&expected));
490    ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1));
491    ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "open"));
492    ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo"));
493    ASSERT_EQ(true, SetDictKeyValue(&expected, "oflag", O_RDONLY));
494
495    PP_Var response;
496    ASSERT_EQ(true, CreateDict(&response));
497    ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1));
498    ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
499    ASSERT_EQ(true, SetDictKeyValue(&response, "fd", fd));
500
501    Expect(expected, response);
502  }
503
504  virtual void TearDown() {
505    JsFsTest::TearDown();
506  }
507
508  void OpenNode() {
509    EXPECT_EQ(0, fs_->Open(Path("/foo"), O_RDONLY, &node_));
510    EXPECT_EQ(fd, sdk_util::static_scoped_ref_cast<JsFsNode>(node_)->fd());
511  }
512
513 protected:
514  ScopedNode node_;
515};
516
517const int JsFsNodeTest::fd = 123;
518
519}  // namespace
520
521TEST_F(JsFsTest, Open) {
522  PP_Var expected;
523  ASSERT_EQ(true, CreateDict(&expected));
524  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1));
525  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "open"));
526  ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo"));
527  ASSERT_EQ(true, SetDictKeyValue(&expected, "oflag", O_RDONLY));
528
529  PP_Var response;
530  ASSERT_EQ(true, CreateDict(&response));
531  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1));
532  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
533  ASSERT_EQ(true, SetDictKeyValue(&response, "fd", 123));
534
535  Expect(expected, response);
536  StartJsThread();
537
538  ScopedNode node;
539  EXPECT_EQ(0, fs_->Open(Path("/foo"), O_RDONLY, &node));
540  EXPECT_EQ(123, sdk_util::static_scoped_ref_cast<JsFsNode>(node)->fd());
541}
542
543TEST_F(JsFsTest, Unlink) {
544  PP_Var expected;
545  ASSERT_EQ(true, CreateDict(&expected));
546  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1));
547  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "unlink"));
548  ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo"));
549
550  PP_Var response;
551  ASSERT_EQ(true, CreateDict(&response));
552  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1));
553  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
554
555  Expect(expected, response);
556  StartJsThread();
557
558  EXPECT_EQ(0, fs_->Unlink(Path("/foo")));
559}
560
561TEST_F(JsFsTest, Mkdir) {
562  PP_Var expected;
563  ASSERT_EQ(true, CreateDict(&expected));
564  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1));
565  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "mkdir"));
566  ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo"));
567  ASSERT_EQ(true, SetDictKeyValue(&expected, "mode", 0644));
568
569  PP_Var response;
570  ASSERT_EQ(true, CreateDict(&response));
571  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1));
572  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
573
574  Expect(expected, response);
575  StartJsThread();
576
577  EXPECT_EQ(0, fs_->Mkdir(Path("/foo"), 0644));
578}
579
580TEST_F(JsFsTest, Rmdir) {
581  PP_Var expected;
582  ASSERT_EQ(true, CreateDict(&expected));
583  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1));
584  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "rmdir"));
585  ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo"));
586
587  PP_Var response;
588  ASSERT_EQ(true, CreateDict(&response));
589  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1));
590  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
591
592  Expect(expected, response);
593  StartJsThread();
594
595  EXPECT_EQ(0, fs_->Rmdir(Path("/foo")));
596}
597
598TEST_F(JsFsTest, Remove) {
599  PP_Var expected;
600  ASSERT_EQ(true, CreateDict(&expected));
601  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1));
602  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "remove"));
603  ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo"));
604
605  PP_Var response;
606  ASSERT_EQ(true, CreateDict(&response));
607  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1));
608  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
609
610  Expect(expected, response);
611  StartJsThread();
612
613  EXPECT_EQ(0, fs_->Remove(Path("/foo")));
614}
615
616TEST_F(JsFsTest, Rename) {
617  PP_Var expected;
618  ASSERT_EQ(true, CreateDict(&expected));
619  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1));
620  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "rename"));
621  ASSERT_EQ(true, SetDictKeyValue(&expected, "old", "/foo"));
622  ASSERT_EQ(true, SetDictKeyValue(&expected, "new", "/bar"));
623
624  PP_Var response;
625  ASSERT_EQ(true, CreateDict(&response));
626  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1));
627  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
628
629  Expect(expected, response);
630  StartJsThread();
631
632  EXPECT_EQ(0, fs_->Rename(Path("/foo"), Path("/bar")));
633}
634
635TEST_F(JsFsNodeTest, GetStat) {
636  PP_Var expected;
637  ASSERT_EQ(true, CreateDict(&expected));
638  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2));
639  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "fstat"));
640  ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd));
641
642  PP_Var response;
643  ASSERT_EQ(true, CreateDict(&response));
644  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2));
645  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
646  ASSERT_EQ(true, SetDictKeyValue(&response, "st_ino", (int64_t) 1));
647  ASSERT_EQ(true, SetDictKeyValue(&response, "st_mode", 2));
648  ASSERT_EQ(true, SetDictKeyValue(&response, "st_nlink", 3));
649  ASSERT_EQ(true, SetDictKeyValue(&response, "st_uid", 4));
650  ASSERT_EQ(true, SetDictKeyValue(&response, "st_gid", 5));
651  ASSERT_EQ(true, SetDictKeyValue(&response, "st_rdev", (int64_t) 6));
652  ASSERT_EQ(true, SetDictKeyValue(&response, "st_size", (int64_t) 7));
653  ASSERT_EQ(true, SetDictKeyValue(&response, "st_blksize", 8));
654  ASSERT_EQ(true, SetDictKeyValue(&response, "st_blocks", 9));
655  ASSERT_EQ(true, SetDictKeyValue(&response, "st_atime", (int64_t) 10));
656  ASSERT_EQ(true, SetDictKeyValue(&response, "st_mtime", (int64_t) 11));
657  ASSERT_EQ(true, SetDictKeyValue(&response, "st_ctime", (int64_t) 12));
658
659  Expect(expected, response);
660  StartJsThread();
661  OpenNode();
662
663  struct stat statbuf;
664  EXPECT_EQ(0, node_->GetStat(&statbuf));
665  EXPECT_EQ(fs_->dev(), statbuf.st_dev);
666  EXPECT_EQ(1, statbuf.st_ino);
667  EXPECT_EQ(2, statbuf.st_mode);
668  EXPECT_EQ(3, statbuf.st_nlink);
669  EXPECT_EQ(4, statbuf.st_uid);
670  EXPECT_EQ(5, statbuf.st_gid);
671  EXPECT_EQ(6, statbuf.st_rdev);
672  EXPECT_EQ(7, statbuf.st_size);
673  EXPECT_EQ(8, statbuf.st_blksize);
674  EXPECT_EQ(9, statbuf.st_blocks);
675  EXPECT_EQ(10, statbuf.st_atime);
676  EXPECT_EQ(11, statbuf.st_mtime);
677  EXPECT_EQ(12, statbuf.st_ctime);
678}
679
680TEST_F(JsFsNodeTest, FSync) {
681  PP_Var expected;
682  ASSERT_EQ(true, CreateDict(&expected));
683  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2));
684  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "fsync"));
685  ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd));
686
687  PP_Var response;
688  ASSERT_EQ(true, CreateDict(&response));
689  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2));
690  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
691
692  Expect(expected, response);
693  StartJsThread();
694  OpenNode();
695
696  EXPECT_EQ(0, node_->FSync());
697}
698
699TEST_F(JsFsNodeTest, FTruncate) {
700  PP_Var expected;
701  ASSERT_EQ(true, CreateDict(&expected));
702  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2));
703  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "ftruncate"));
704  ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd));
705  ASSERT_EQ(true, SetDictKeyValue(&expected, "length", 0));
706
707  PP_Var response;
708  ASSERT_EQ(true, CreateDict(&response));
709  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2));
710  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
711
712  Expect(expected, response);
713  StartJsThread();
714  OpenNode();
715
716  EXPECT_EQ(0, node_->FTruncate(0));
717}
718
719TEST_F(JsFsNodeTest, Read) {
720  const size_t kReadLength = 100;
721
722  PP_Var expected;
723  ASSERT_EQ(true, CreateDict(&expected));
724  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2));
725  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "pread"));
726  ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd));
727  ASSERT_EQ(true,
728            SetDictKeyValue(&expected, "nbyte", static_cast<int>(kReadLength)));
729  ASSERT_EQ(true, SetDictKeyValue(&expected, "offset", 200));
730
731  PP_Var response;
732  ASSERT_EQ(true, CreateDict(&response));
733  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2));
734  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
735  ASSERT_EQ(
736      true,
737      SetDictKeyValue(&response, "buf", CreateDummyArrayBuffer(kReadLength)));
738
739  Expect(expected, response);
740  StartJsThread();
741  OpenNode();
742
743  HandleAttr attr;
744  attr.offs = 200;
745  uint8_t buf[kReadLength];
746  int bytes_read;
747  EXPECT_EQ(0, node_->Read(attr, buf, kReadLength, &bytes_read));
748  EXPECT_EQ(kReadLength, bytes_read);
749  EXPECT_TRUE(EqualsDummyArrayBuffer(buf, kReadLength));
750}
751
752TEST_F(JsFsNodeTest, Write) {
753  const size_t kWriteLength = 100;
754
755  PP_Var expected;
756  ASSERT_EQ(true, CreateDict(&expected));
757  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2));
758  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "pwrite"));
759  ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd));
760  ASSERT_EQ(
761      true,
762      SetDictKeyValue(&expected, "buf", CreateDummyArrayBuffer(kWriteLength)));
763  ASSERT_EQ(
764      true,
765      SetDictKeyValue(&expected, "nbyte", static_cast<int>(kWriteLength)));
766  ASSERT_EQ(true, SetDictKeyValue(&expected, "offset", 200));
767
768  PP_Var response;
769  ASSERT_EQ(true, CreateDict(&response));
770  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2));
771  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
772  ASSERT_EQ(true, SetDictKeyValue(&response, "nwrote", kWriteLength));
773
774  Expect(expected, response);
775  StartJsThread();
776  OpenNode();
777
778  HandleAttr attr;
779  attr.offs = 200;
780
781  uint8_t buf[kWriteLength];
782  FillDummyBuffer(buf, kWriteLength);
783
784  int bytes_written;
785  EXPECT_EQ(0, node_->Write(attr, buf, kWriteLength, &bytes_written));
786  EXPECT_EQ(kWriteLength, bytes_written);
787}
788
789TEST_F(JsFsNodeTest, GetDents) {
790  PP_Var expected;
791  ASSERT_EQ(true, CreateDict(&expected));
792  ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2));
793  ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "getdents"));
794  ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd));
795  ASSERT_EQ(true, SetDictKeyValue(&expected, "offs", 0));
796  ASSERT_EQ(true, SetDictKeyValue(&expected, "count", 2));
797
798  PP_Var entry0;
799  ASSERT_EQ(true, CreateDict(&entry0));
800  ASSERT_EQ(true, SetDictKeyValue(&entry0, "d_ino", 2));
801  ASSERT_EQ(true, SetDictKeyValue(&entry0, "d_name", "."));
802  PP_Var entry1;
803  ASSERT_EQ(true, CreateDict(&entry1));
804  ASSERT_EQ(true, SetDictKeyValue(&entry1, "d_ino", 3));
805  ASSERT_EQ(true, SetDictKeyValue(&entry1, "d_name", ".."));
806  PP_Var array;
807  ASSERT_EQ(true, CreateArray(&array));
808  ASSERT_EQ(true, SetArrayValue(&array, 0, entry0));
809  ASSERT_EQ(true, SetArrayValue(&array, 1, entry1));
810  PP_Var response;
811  ASSERT_EQ(true, CreateDict(&response));
812  ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2));
813  ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0));
814  ASSERT_EQ(true, SetDictKeyValue(&response, "dirents", array));
815
816  Expect(expected, response);
817  StartJsThread();
818  OpenNode();
819
820  dirent buf[2];
821  int bytes_written;
822  EXPECT_EQ(0, node_->GetDents(0, buf, sizeof(dirent) * 2, &bytes_written));
823  EXPECT_EQ(sizeof(dirent) * 2, bytes_written);
824  EXPECT_EQ(2, buf[0].d_ino);
825  EXPECT_EQ(sizeof(dirent), buf[0].d_off);
826  EXPECT_EQ(sizeof(dirent), buf[0].d_reclen);
827  EXPECT_STREQ(".", buf[0].d_name);
828  EXPECT_EQ(3, buf[1].d_ino);
829  EXPECT_EQ(sizeof(dirent), buf[1].d_off);
830  EXPECT_EQ(sizeof(dirent), buf[1].d_reclen);
831  EXPECT_STREQ("..", buf[1].d_name);
832}
833