1 // Copyright 2022 Google LLC
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 // https://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 #define _GNU_SOURCE 1
16 #include "../turbojpeg_sapi.h" // NOLINT(build/include)
17
18 #include <turbojpeg.h>
19
20 #include <cerrno>
21 #include <cstdlib>
22 #include <fstream>
23 #include <iostream>
24
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "sandboxed_api/testing.h"
28 #include "sandboxed_api/util/fileops.h"
29 #include "sandboxed_api/util/path.h"
30 #include "sandboxed_api/util/status_matchers.h"
31 #include "turbojpeg_sapi.sapi.h" // NOLINT(build/include)
32
33 namespace {
34
35 using ::sapi::IsOk;
36 using ::testing::Eq;
37 using ::testing::Gt;
38 using ::testing::Not;
39 using ::testing::NotNull;
40 using ::testing::StrEq;
41
42 class TurboJpegSapiSandboxTest : public testing::Test {
43 protected:
SetUpTestSuite()44 static void SetUpTestSuite() {
45 ASSERT_THAT(getenv("TEST_FILES_DIR"), NotNull());
46 sandbox_ = new TurboJpegSapiSandbox();
47 ASSERT_THAT(sandbox_->Init(), IsOk());
48 api_ = new turbojpeg_sapi::TurboJPEGApi(sandbox_);
49 }
TearDownTestSuite()50 static void TearDownTestSuite() {
51 delete api_;
52 delete sandbox_;
53 }
54
GetTurboJpegErrorStr(sapi::v::Ptr * handle)55 static std::string GetTurboJpegErrorStr(sapi::v::Ptr* handle) {
56 auto errmsg_ptr = api_->tjGetErrorStr2(handle);
57 if (!errmsg_ptr.ok()) return "Error getting error message";
58 auto errmsg =
59 sandbox_->GetCString(sapi::v::RemotePtr(errmsg_ptr.value()), 256);
60 if (!errmsg.ok()) return "Error getting error message";
61 return errmsg.value();
62 }
63 static turbojpeg_sapi::TurboJPEGApi* api_;
64 static TurboJpegSapiSandbox* sandbox_;
65 };
66
67 turbojpeg_sapi::TurboJPEGApi* TurboJpegSapiSandboxTest::api_;
68 TurboJpegSapiSandbox* TurboJpegSapiSandboxTest::sandbox_;
69
GetTestFilePath(const std::string & filename)70 std::string GetTestFilePath(const std::string& filename) {
71 return sapi::file::JoinPath(getenv("TEST_FILES_DIR"), filename);
72 }
73
GetStreamSize(std::ifstream & stream)74 std::streamsize GetStreamSize(std::ifstream& stream) {
75 stream.seekg(0, std::ios_base::end);
76 std::streamsize ssize = stream.tellg();
77 stream.seekg(0, std::ios_base::beg);
78
79 return ssize;
80 }
81
ReadFile(const std::string & in_file,size_t expected_size=SIZE_MAX)82 absl::StatusOr<std::vector<uint8_t>> ReadFile(const std::string& in_file,
83 size_t expected_size = SIZE_MAX) {
84 std::ifstream f(GetTestFilePath(in_file));
85 if (!f.is_open()) {
86 return absl::UnavailableError("File could not be opened");
87 }
88 std::streamsize ssize = GetStreamSize(f);
89 if (expected_size != SIZE_MAX && ssize != expected_size) {
90 return absl::UnavailableError("Incorrect size of file");
91 }
92 std::vector<uint8_t> inbuf(ssize);
93 f.read(reinterpret_cast<char*>(inbuf.data()), ssize);
94 if (ssize != f.gcount()) {
95 return absl::UnavailableError("Premature end of file");
96 }
97 if (f.fail() || f.eof()) {
98 return absl::UnavailableError("Error reading file");
99 }
100 return inbuf;
101 }
102
TEST_F(TurboJpegSapiSandboxTest,Compressor)103 TEST_F(TurboJpegSapiSandboxTest, Compressor) {
104 absl::StatusOr<void*> compression_handle_raw = api_->tjInitCompress();
105 ASSERT_THAT(compression_handle_raw, IsOk());
106 ASSERT_THAT(compression_handle_raw.value(), NotNull());
107 sapi::v::RemotePtr compression_handle{compression_handle_raw.value()};
108 auto result = ReadFile("sample.rgb", 12 * 67 * 3);
109 ASSERT_THAT(result, IsOk());
110 sapi::v::Array array(result->data(), result->size());
111
112 sapi::v::GenericPtr buffer;
113 {
114 sapi::v::ULong length{0};
115 auto result = api_->tjCompress2(&compression_handle, array.PtrBefore(), 12,
116 36, 67, TJPF_RGB, buffer.PtrAfter(),
117 length.PtrBoth(), TJSAMP_444, 10, 0);
118 ASSERT_THAT(result, IsOk());
119 ASSERT_THAT(result.value(), Eq(0))
120 << "Error from sandboxee: "
121 << GetTurboJpegErrorStr(&compression_handle);
122 ASSERT_TRUE(buffer.GetValue());
123 ASSERT_TRUE(buffer.GetRemote());
124 ASSERT_THAT(length.GetValue(), Gt(0));
125 }
126 auto value = buffer.GetValue();
127
128 auto destroy_result = api_->tjDestroy(&compression_handle);
129 ASSERT_THAT(destroy_result, IsOk());
130 ASSERT_THAT(destroy_result.value(), Eq(0));
131 }
132
TEST_F(TurboJpegSapiSandboxTest,Decompressor)133 TEST_F(TurboJpegSapiSandboxTest, Decompressor) {
134 absl::StatusOr<void*> decompression_handle_raw = api_->tjInitDecompress();
135 ASSERT_THAT(decompression_handle_raw, IsOk());
136 ASSERT_THAT(decompression_handle_raw.value(), NotNull());
137 sapi::v::RemotePtr decompression_handle{decompression_handle_raw.value()};
138 auto result = ReadFile("sample.jpeg");
139 ASSERT_THAT(result, IsOk());
140 sapi::v::Array array(result->data(), result->size());
141
142 sapi::v::Int width{0};
143 sapi::v::Int height{0};
144 sapi::v::Int subsamp{0};
145 sapi::v::Int colorspace{0};
146 auto decompress_result = api_->tjDecompressHeader3(
147 &decompression_handle, array.PtrBefore(), result->size(),
148 width.PtrAfter(), height.PtrAfter(), subsamp.PtrAfter(),
149 colorspace.PtrAfter());
150 ASSERT_THAT(decompress_result, IsOk());
151 ASSERT_THAT(decompress_result.value(), Eq(0))
152 << "Error from sandboxee: "
153 << GetTurboJpegErrorStr(&decompression_handle);
154
155 ASSERT_THAT(width.GetValue(), Eq(67));
156 ASSERT_THAT(height.GetValue(), Eq(12));
157 ASSERT_THAT(subsamp.GetValue(), Eq(TJSAMP_GRAY));
158 ASSERT_THAT(colorspace.GetValue(), Eq(TJCS_GRAY));
159
160 auto arr = sapi::v::Array<unsigned char>(12 * 67 * 3);
161 decompress_result = api_->tjDecompress2(
162 &decompression_handle, array.PtrBefore(), result->size(), arr.PtrAfter(),
163 12, 36, 67, TJCS_RGB, 0);
164 ASSERT_THAT(decompress_result, IsOk());
165 EXPECT_THAT(decompress_result.value(), Eq(0))
166 << "Error from sandboxee: "
167 << GetTurboJpegErrorStr(&decompression_handle);
168
169 decompress_result = api_->tjDestroy(&decompression_handle);
170 ASSERT_THAT(decompress_result, IsOk());
171 ASSERT_THAT(decompress_result.value(), Eq(0));
172 }
173 } // namespace
174