Skip to content

Commit

Permalink
Add tests for POSIX write.
Browse files Browse the repository at this point in the history
Change-Id: I14387feb6cc34bf29a0ed9a2b5f9675f08db8f67
  • Loading branch information
yjzhang111 committed May 9, 2024
1 parent 79adf53 commit 69603e7
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 0 deletions.
18 changes: 18 additions & 0 deletions starboard/common/file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,22 @@ bool SbFileDeleteRecursive(const char* path, bool preserve_root) {
return false;
}

ssize_t WriteAll(int file, const void* data, int size) {
if (file < 0 || size < 0) {
return -1;
}
ssize_t bytes_written = 0;
ssize_t rv;
do {
rv = write(file, reinterpret_cast<const char*>(data) + bytes_written,
size - bytes_written);
if (rv <= 0) {
break;
}
bytes_written += rv;
} while (bytes_written < size);

return bytes_written ? bytes_written : rv;
}

} // namespace starboard
2 changes: 2 additions & 0 deletions starboard/common/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ void RecordFileWriteStat(int write_file_result);
// |preserve_root|: Whether or not the root directory should be preserved.
bool SbFileDeleteRecursive(const char* path, bool preserve_root);

ssize_t WriteAll(int fd, const void* data, int size);

// A class that opens an SbFile in its constructor and closes it in its
// destructor, so the file is open for the lifetime of the object. Member
// functions call the corresponding SbFile function.
Expand Down
169 changes: 169 additions & 0 deletions starboard/nplb/posix_compliance/posix_file_write_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright 2024 The Cobalt Authors. All Rights Reserved.
//
// 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.

// Writing is partially tested by some of the file helpers that create files
// for the tests to operate on.

#include <fcntl.h>
#include <unistd.h>

#include <string>

#include "starboard/common/file.h"
#include "starboard/nplb/file_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace starboard {
namespace nplb {
namespace {

// Sets up an empty test fixture, required for typed tests.
template <class SbFileWriteType>
class PosixFileWriteTest : public testing::Test {};

class PosixFileWriter {
public:
static ssize_t Write(int file, char* data, size_t size) {
return write(file, data, size);
}
};

class PosixFileWriterAll {
public:
static ssize_t Write(int file, char* data, size_t size) {
return WriteAll(file, data, size);
}
};

typedef testing::Types<PosixFileWriter, PosixFileWriterAll>
PosixFileWriteTestTypes;

TYPED_TEST_CASE(PosixFileWriteTest, PosixFileWriteTestTypes);

const int kBufferLength = 16 * 1024;

TYPED_TEST(PosixFileWriteTest, InvalidFileErrors) {
char buffer[kBufferLength] = {0};
int result = TypeParam::Write(-1, buffer, kBufferLength);
EXPECT_EQ(-1, result);
}

TYPED_TEST(PosixFileWriteTest, BasicWriting) {
// Choose a file size that is not an even multiple of the buffer size, but
// is over several times the size of the buffer.
const int kFileSize = kBufferLength * 16 / 3;
ScopedRandomFile random_file(0, ScopedRandomFile::kDontCreate);
const std::string& filename = random_file.filename();

int file = open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC);
ASSERT_TRUE(file >= 0);

// Create a bigger buffer than necessary, so we can test the memory around
// the portion given to SbFileRead.
char buffer[kBufferLength] = {0};

// Initialize to some arbitrary pattern so we can verify it later.
for (int i = 0; i < kBufferLength; ++i) {
buffer[i] = static_cast<char>(i & 0xFF);
}

// Read and check the whole file.
int total = 0;
while (true) {
if (total == kFileSize) {
break;
}

int remaining = kFileSize - total;
int to_write = remaining < kBufferLength ? remaining : kBufferLength;
int bytes_written =
TypeParam::Write(file, buffer + (total % kBufferLength), to_write);

// Check that we didn't write more than the buffer size.
EXPECT_GE(to_write, bytes_written);

// Check that we didn't get an error.
ASSERT_LT(0, bytes_written);

total += bytes_written;
EXPECT_EQ(total, lseek(file, 0, SEEK_CUR));
}

// Tests reading and writing from same opened file.
int result = fsync(file);
ASSERT_TRUE(result == 0);
int position = static_cast<int>(lseek(file, 0, SEEK_SET));
ASSERT_EQ(0, position);

// Read and check the whole file.
total = 0;
int previous_total = 0;
while (true) {
int bytes_read = ReadAll(file, buffer, kBufferLength);
if (bytes_read == 0) {
break;
}

// Check that we didn't read more than the buffer size.
EXPECT_GE(kBufferLength, bytes_read);

// Check that we didn't get an error.
ASSERT_LT(0, bytes_read);

// Do some accounting to check later.
previous_total = total;
total += bytes_read;

ScopedRandomFile::ExpectPattern(previous_total, buffer, bytes_read,
__LINE__);
}

// Check that we read the whole file.
EXPECT_EQ(kFileSize, total);

result = close(file);
EXPECT_TRUE(result == 0);
}

TYPED_TEST(PosixFileWriteTest, WriteZeroBytes) {
ScopedRandomFile random_file(0, ScopedRandomFile::kDontCreate);
const std::string& filename = random_file.filename();

int file = open(filename.c_str(), O_CREAT | O_EXCL | O_WRONLY);
ASSERT_TRUE(file >= 0);

char buffer[kBufferLength] = {0};

// Write zero bytes.
for (int i = 0; i < 10; ++i) {
int bytes_written = TypeParam::Write(file, buffer, 0);
EXPECT_EQ(0, bytes_written);
}

int result = close(file);
EXPECT_TRUE(result == 0);

file = open(filename.c_str(), O_RDONLY);
ASSERT_TRUE(file >= 0);
struct stat info;
result = fstat(file, &info);
EXPECT_TRUE(result == 0);
EXPECT_EQ(0, info.st_size);
result = close(file);
EXPECT_TRUE(result == 0);
}

} // namespace
} // namespace nplb
} // namespace starboard
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ ssize_t __abi_wrap_read(int fildes, void* buf, size_t nbyte);
ssize_t read(int fildes, void* buf, size_t nbyte) {
return __abi_wrap_read(fildes, buf, nbyte);
}

ssize_t __abi_wrap_write(int fildes, const void* buf, size_t nbyte);

ssize_t write(int fildes, const void* buf, size_t nbyte) {
return __abi_wrap_write(fildes, buf, nbyte);
}
}

#endif // SB_API_VERSION >= 16
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ ssize_t __abi_wrap_read(int fildes, void* buf, size_t nbyte) {
return read(fildes, buf, nbyte);
}

ssize_t __abi_wrap_write(int fildes, const void* buf, size_t nbyte) {
return write(fildes, buf, nbyte);
}

#endif // SB_API_VERSION >= 16
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ SB_EXPORT musl_off_t __abi_wrap_lseek(int fildes,

SB_EXPORT ssize_t __abi_wrap_read(int fildes, void* buf, size_t nbyte);

SB_EXPORT ssize_t __abi_wrap_write(int fildes, const void* buf, size_t nbyte);

#ifdef __cplusplus
} // extern "C"
#endif
Expand Down
3 changes: 3 additions & 0 deletions starboard/shared/win32/posix_emu/include/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ off_t sb_lseek(int fd, off_t offset, int origin);
ssize_t sb_read(int fildes, void* buf, size_t nbyte);
#define read sb_read

ssize_t sb_write(int fd, const void* buf, size_t nbyte);
#define write sb_write

int usleep(unsigned int useconds);

#ifdef __cplusplus
Expand Down
8 changes: 8 additions & 0 deletions starboard/shared/win32/posix_emu/socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ ssize_t sb_read(int fd, void* buf, size_t nbyte) {
return _read(handle.file, buf, nbyte);
}

ssize_t sb_write(int fd, const void* buf, size_t nbyte) {
FileOrSocket handle = handle_db_get(fd, false);
if (!handle.is_file) {
return -1;
}
return write(handle.file, buf, nbyte);
}

int sb_bind(int socket, const struct sockaddr* address, socklen_t address_len) {
SOCKET socket_handle = handle_db_get(socket, false).socket;
if (socket_handle == INVALID_SOCKET) {
Expand Down
19 changes: 19 additions & 0 deletions third_party/musl/src/starboard/network/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,25 @@ ssize_t read(int fildes, void* buf, size_t nbyte) {
return (ssize_t)SbFileRead(fileOrSock->file, buf, (int)nbyte);
}

ssize_t write(int fildes, const void* buf, size_t nbyte) {
if (fildes < 0) {
errno = EBADF;
return -1;
}

FileOrSocket* fileOrSock = NULL;
if (get(fildes, false, &fileOrSock) != 0) {
errno = EBADF;
return -1;
}

if (fileOrSock == NULL || !fileOrSock->is_file) {
errno = EBADF;
return -1;
}
return (ssize_t)SbFileWrite(fileOrSock->file, buf, (int)nbyte);
}

int socket(int domain, int type, int protocol){
int address_type, socket_protocol;
switch (domain){
Expand Down

0 comments on commit 69603e7

Please sign in to comment.