1*ec63e07aSXin Li // Copyright 2020 Google LLC
2*ec63e07aSXin Li //
3*ec63e07aSXin Li // Licensed under the Apache License, Version 2.0 (the "License");
4*ec63e07aSXin Li // you may not use this file except in compliance with the License.
5*ec63e07aSXin Li // You may obtain a copy of the License at
6*ec63e07aSXin Li //
7*ec63e07aSXin Li // https://www.apache.org/licenses/LICENSE-2.0
8*ec63e07aSXin Li //
9*ec63e07aSXin Li // Unless required by applicable law or agreed to in writing, software
10*ec63e07aSXin Li // distributed under the License is distributed on an "AS IS" BASIS,
11*ec63e07aSXin Li // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*ec63e07aSXin Li // See the License for the specific language governing permissions and
13*ec63e07aSXin Li // limitations under the License.
14*ec63e07aSXin Li
15*ec63e07aSXin Li #include <unistd.h>
16*ec63e07aSXin Li
17*ec63e07aSXin Li #include <fstream>
18*ec63e07aSXin Li #include <iostream>
19*ec63e07aSXin Li #include <memory>
20*ec63e07aSXin Li #include <streambuf>
21*ec63e07aSXin Li #include <string>
22*ec63e07aSXin Li
23*ec63e07aSXin Li #include "gtest/gtest.h"
24*ec63e07aSXin Li #include "contrib/jsonnet/jsonnet_base_sandbox.h"
25*ec63e07aSXin Li #include "sandboxed_api/util/path.h"
26*ec63e07aSXin Li #include "sandboxed_api/util/status_matchers.h"
27*ec63e07aSXin Li
28*ec63e07aSXin Li namespace {
29*ec63e07aSXin Li
30*ec63e07aSXin Li class JsonnetTest : public ::testing::Test {
31*ec63e07aSXin Li protected:
32*ec63e07aSXin Li enum Evaluation { kBase, kMultipleFiles, kYamlStream };
33*ec63e07aSXin Li
SetUp()34*ec63e07aSXin Li void SetUp() override {
35*ec63e07aSXin Li // Get paths to where input and output is stored.
36*ec63e07aSXin Li char buffer[256];
37*ec63e07aSXin Li int error = readlink("/proc/self/exe", buffer, 256);
38*ec63e07aSXin Li ASSERT_GE(error, 0);
39*ec63e07aSXin Li
40*ec63e07aSXin Li std::pair<absl::string_view, absl::string_view> parts_of_path =
41*ec63e07aSXin Li sapi::file::SplitPath(buffer);
42*ec63e07aSXin Li absl::string_view binary_path = parts_of_path.first;
43*ec63e07aSXin Li
44*ec63e07aSXin Li std::string input_path =
45*ec63e07aSXin Li sapi::file::JoinPath(binary_path, "tests_input", "dummy_input");
46*ec63e07aSXin Li std::string output_path =
47*ec63e07aSXin Li sapi::file::JoinPath(binary_path, "tests_output", "dummy_input");
48*ec63e07aSXin Li
49*ec63e07aSXin Li // Set up sandbox and api.
50*ec63e07aSXin Li sandbox_ = std::make_unique<JsonnetBaseSandbox>(input_path, output_path);
51*ec63e07aSXin Li ASSERT_THAT(sandbox_->Init(), sapi::IsOk());
52*ec63e07aSXin Li api_ = std::make_unique<JsonnetApi>(sandbox_.get());
53*ec63e07aSXin Li
54*ec63e07aSXin Li // Initialize library's main structure.
55*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(JsonnetVm * vm_ptr, api_->c_jsonnet_make());
56*ec63e07aSXin Li vm_ = std::make_unique<sapi::v::RemotePtr>(vm_ptr);
57*ec63e07aSXin Li }
58*ec63e07aSXin Li
TearDown()59*ec63e07aSXin Li void TearDown() override {
60*ec63e07aSXin Li if (jsonnet_vm_was_used_) {
61*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
62*ec63e07aSXin Li char* result, api_->c_jsonnet_realloc(vm_.get(), output_.get(), 0));
63*ec63e07aSXin Li }
64*ec63e07aSXin Li ASSERT_THAT(api_->c_jsonnet_destroy(vm_.get()), sapi::IsOk());
65*ec63e07aSXin Li if (input_was_read_) {
66*ec63e07aSXin Li ASSERT_THAT(api_->c_free_input(input_.get()), sapi::IsOk());
67*ec63e07aSXin Li }
68*ec63e07aSXin Li }
69*ec63e07aSXin Li
70*ec63e07aSXin Li // Reads input from file.
71*ec63e07aSXin Li void ReadInput(const char* filename);
72*ec63e07aSXin Li
73*ec63e07aSXin Li // Evaluates jsonnet code.
74*ec63e07aSXin Li void EvaluateJsonnetCode(Evaluation type, bool expected_correct);
75*ec63e07aSXin Li
76*ec63e07aSXin Li // Writes output to file.
77*ec63e07aSXin Li void WriteOutput(const char* filename_or_directory, Evaluation type);
78*ec63e07aSXin Li
79*ec63e07aSXin Li // Reads the output written to a file by library function / expected output.
80*ec63e07aSXin Li std::string ReadOutput(const char* filename);
81*ec63e07aSXin Li
82*ec63e07aSXin Li std::unique_ptr<JsonnetBaseSandbox> sandbox_;
83*ec63e07aSXin Li std::unique_ptr<JsonnetApi> api_;
84*ec63e07aSXin Li std::unique_ptr<sapi::v::RemotePtr> input_;
85*ec63e07aSXin Li std::unique_ptr<sapi::v::RemotePtr> output_;
86*ec63e07aSXin Li std::unique_ptr<sapi::v::RemotePtr> vm_;
87*ec63e07aSXin Li
88*ec63e07aSXin Li std::string input_filename_in_sandboxee_;
89*ec63e07aSXin Li bool jsonnet_vm_was_used_ = false;
90*ec63e07aSXin Li bool input_was_read_ = false;
91*ec63e07aSXin Li };
92*ec63e07aSXin Li
ReadInput(const char * filename)93*ec63e07aSXin Li void JsonnetTest::ReadInput(const char* filename) {
94*ec63e07aSXin Li std::string in_file_in_sandboxee(std::string("/input/") +
95*ec63e07aSXin Li basename(const_cast<char*>(&filename[0])));
96*ec63e07aSXin Li input_filename_in_sandboxee_ = std::move(in_file_in_sandboxee);
97*ec63e07aSXin Li sapi::v::ConstCStr in_file_var(input_filename_in_sandboxee_.c_str());
98*ec63e07aSXin Li
99*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(char* input_ptr,
100*ec63e07aSXin Li api_->c_read_input(0, in_file_var.PtrBefore()));
101*ec63e07aSXin Li input_ = std::make_unique<sapi::v::RemotePtr>(input_ptr);
102*ec63e07aSXin Li
103*ec63e07aSXin Li input_was_read_ = true;
104*ec63e07aSXin Li }
105*ec63e07aSXin Li
EvaluateJsonnetCode(Evaluation type,bool expected_correct)106*ec63e07aSXin Li void JsonnetTest::EvaluateJsonnetCode(Evaluation type, bool expected_correct) {
107*ec63e07aSXin Li sapi::v::ConstCStr in_file_var(input_filename_in_sandboxee_.c_str());
108*ec63e07aSXin Li sapi::v::Int error;
109*ec63e07aSXin Li char* output_ptr;
110*ec63e07aSXin Li
111*ec63e07aSXin Li switch (type) {
112*ec63e07aSXin Li case kBase: {
113*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
114*ec63e07aSXin Li output_ptr,
115*ec63e07aSXin Li api_->c_jsonnet_evaluate_snippet(vm_.get(), in_file_var.PtrBefore(),
116*ec63e07aSXin Li input_.get(), error.PtrAfter()));
117*ec63e07aSXin Li break;
118*ec63e07aSXin Li }
119*ec63e07aSXin Li
120*ec63e07aSXin Li case kMultipleFiles: {
121*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
122*ec63e07aSXin Li output_ptr, api_->c_jsonnet_evaluate_snippet_multi(
123*ec63e07aSXin Li vm_.get(), in_file_var.PtrBefore(), input_.get(),
124*ec63e07aSXin Li error.PtrAfter()));
125*ec63e07aSXin Li break;
126*ec63e07aSXin Li }
127*ec63e07aSXin Li
128*ec63e07aSXin Li case kYamlStream: {
129*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
130*ec63e07aSXin Li output_ptr, api_->c_jsonnet_evaluate_snippet_stream(
131*ec63e07aSXin Li vm_.get(), in_file_var.PtrBefore(), input_.get(),
132*ec63e07aSXin Li error.PtrAfter()));
133*ec63e07aSXin Li break;
134*ec63e07aSXin Li }
135*ec63e07aSXin Li }
136*ec63e07aSXin Li
137*ec63e07aSXin Li if (expected_correct) {
138*ec63e07aSXin Li ASSERT_THAT(error.GetValue(), testing::Eq(0));
139*ec63e07aSXin Li } else {
140*ec63e07aSXin Li ASSERT_THAT(error.GetValue(), testing::Eq(1));
141*ec63e07aSXin Li }
142*ec63e07aSXin Li
143*ec63e07aSXin Li output_ = std::make_unique<sapi::v::RemotePtr>(output_ptr);
144*ec63e07aSXin Li
145*ec63e07aSXin Li jsonnet_vm_was_used_ = true;
146*ec63e07aSXin Li }
147*ec63e07aSXin Li
WriteOutput(const char * filename_or_directory,Evaluation type)148*ec63e07aSXin Li void JsonnetTest::WriteOutput(const char* filename_or_directory,
149*ec63e07aSXin Li Evaluation type) {
150*ec63e07aSXin Li bool success;
151*ec63e07aSXin Li
152*ec63e07aSXin Li switch (type) {
153*ec63e07aSXin Li case kBase: {
154*ec63e07aSXin Li std::string out_file_in_sandboxee(
155*ec63e07aSXin Li std::string("/output/") +
156*ec63e07aSXin Li basename(const_cast<char*>(&filename_or_directory[0])));
157*ec63e07aSXin Li sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
158*ec63e07aSXin Li
159*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
160*ec63e07aSXin Li success,
161*ec63e07aSXin Li api_->c_write_output_file(output_.get(), out_file_var.PtrBefore()));
162*ec63e07aSXin Li break;
163*ec63e07aSXin Li }
164*ec63e07aSXin Li case kMultipleFiles: {
165*ec63e07aSXin Li std::string out_file_in_sandboxee(std::string("/output/"));
166*ec63e07aSXin Li sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
167*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
168*ec63e07aSXin Li success, api_->c_write_multi_output_files(
169*ec63e07aSXin Li output_.get(), out_file_var.PtrBefore(), false));
170*ec63e07aSXin Li break;
171*ec63e07aSXin Li }
172*ec63e07aSXin Li
173*ec63e07aSXin Li case kYamlStream: {
174*ec63e07aSXin Li std::string out_file_in_sandboxee(
175*ec63e07aSXin Li std::string("/output/") +
176*ec63e07aSXin Li basename(const_cast<char*>(&filename_or_directory[0])));
177*ec63e07aSXin Li sapi::v::ConstCStr out_file_var(out_file_in_sandboxee.c_str());
178*ec63e07aSXin Li SAPI_ASSERT_OK_AND_ASSIGN(
179*ec63e07aSXin Li success,
180*ec63e07aSXin Li api_->c_write_output_stream(output_.get(), out_file_var.PtrBefore()));
181*ec63e07aSXin Li break;
182*ec63e07aSXin Li }
183*ec63e07aSXin Li }
184*ec63e07aSXin Li
185*ec63e07aSXin Li ASSERT_THAT(success, testing::Eq(true));
186*ec63e07aSXin Li }
187*ec63e07aSXin Li
ReadOutput(const char * filename)188*ec63e07aSXin Li std::string JsonnetTest::ReadOutput(const char* filename) {
189*ec63e07aSXin Li std::ifstream input_stream(filename);
190*ec63e07aSXin Li std::string contents((std::istreambuf_iterator<char>(input_stream)),
191*ec63e07aSXin Li std::istreambuf_iterator<char>());
192*ec63e07aSXin Li return contents;
193*ec63e07aSXin Li }
194*ec63e07aSXin Li
195*ec63e07aSXin Li // One file evaluation to one file
TEST_F(JsonnetTest,OneFileNoDependencies)196*ec63e07aSXin Li TEST_F(JsonnetTest, OneFileNoDependencies) {
197*ec63e07aSXin Li constexpr char kInputFile[] = "arith.jsonnet";
198*ec63e07aSXin Li constexpr char kOutputFile[] = "arith_output";
199*ec63e07aSXin Li constexpr char kOutputToRead[] = "tests_output/arith_output";
200*ec63e07aSXin Li constexpr char kOutputToExpect[] = "tests_expected_output/arith.golden";
201*ec63e07aSXin Li
202*ec63e07aSXin Li ReadInput(kInputFile);
203*ec63e07aSXin Li EvaluateJsonnetCode(kBase, true);
204*ec63e07aSXin Li WriteOutput(kOutputFile, kBase);
205*ec63e07aSXin Li
206*ec63e07aSXin Li std::string produced_output = ReadOutput(kOutputToRead);
207*ec63e07aSXin Li std::string expected_output = ReadOutput(kOutputToExpect);
208*ec63e07aSXin Li
209*ec63e07aSXin Li ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
210*ec63e07aSXin Li }
211*ec63e07aSXin Li
212*ec63e07aSXin Li // One file evaluating to one file, dependent on some other files
TEST_F(JsonnetTest,OneFileSomeDependencies)213*ec63e07aSXin Li TEST_F(JsonnetTest, OneFileSomeDependencies) {
214*ec63e07aSXin Li constexpr char kInputFile[] = "negroni.jsonnet";
215*ec63e07aSXin Li constexpr char kOutputFile[] = "negroni_output";
216*ec63e07aSXin Li constexpr char kOutputToRead[] = "tests_output/negroni_output";
217*ec63e07aSXin Li constexpr char kOutputToExpect[] = "tests_expected_output/negroni.golden";
218*ec63e07aSXin Li
219*ec63e07aSXin Li ReadInput(kInputFile);
220*ec63e07aSXin Li EvaluateJsonnetCode(kBase, true);
221*ec63e07aSXin Li WriteOutput(kOutputFile, kBase);
222*ec63e07aSXin Li
223*ec63e07aSXin Li const std::string produced_output = ReadOutput(kOutputToRead);
224*ec63e07aSXin Li const std::string expected_output = ReadOutput(kOutputToExpect);
225*ec63e07aSXin Li
226*ec63e07aSXin Li ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
227*ec63e07aSXin Li }
228*ec63e07aSXin Li
229*ec63e07aSXin Li // One file evaluating to two files
TEST_F(JsonnetTest,MultipleFiles)230*ec63e07aSXin Li TEST_F(JsonnetTest, MultipleFiles) {
231*ec63e07aSXin Li constexpr char kInputFile[] = "multiple_files_example.jsonnet";
232*ec63e07aSXin Li constexpr char kOutputFile[] = "";
233*ec63e07aSXin Li constexpr char kOutputToRead1[] = "tests_output/first_file.json";
234*ec63e07aSXin Li constexpr char kOutputToRead2[] = "tests_output/second_file.json";
235*ec63e07aSXin Li constexpr char kOutputToExpect1[] = "tests_expected_output/first_file.json";
236*ec63e07aSXin Li constexpr char kOutputToExpect2[] = "tests_expected_output/second_file.json";
237*ec63e07aSXin Li
238*ec63e07aSXin Li ReadInput(kInputFile);
239*ec63e07aSXin Li EvaluateJsonnetCode(kMultipleFiles, true);
240*ec63e07aSXin Li WriteOutput(kOutputFile, kMultipleFiles);
241*ec63e07aSXin Li
242*ec63e07aSXin Li const std::string produced_output_1 = ReadOutput(kOutputToRead1);
243*ec63e07aSXin Li const std::string produced_output_2 = ReadOutput(kOutputToRead2);
244*ec63e07aSXin Li const std::string expected_output_1 = ReadOutput(kOutputToExpect1);
245*ec63e07aSXin Li const std::string expected_output_2 = ReadOutput(kOutputToExpect2);
246*ec63e07aSXin Li
247*ec63e07aSXin Li ASSERT_STREQ(produced_output_1.c_str(), expected_output_1.c_str());
248*ec63e07aSXin Li ASSERT_STREQ(produced_output_2.c_str(), expected_output_2.c_str());
249*ec63e07aSXin Li }
250*ec63e07aSXin Li
251*ec63e07aSXin Li // One file evaluating to yaml stream format
TEST_F(JsonnetTest,YamlStream)252*ec63e07aSXin Li TEST_F(JsonnetTest, YamlStream) {
253*ec63e07aSXin Li constexpr char kInputFile[] = "yaml_stream_example.jsonnet";
254*ec63e07aSXin Li constexpr char kOutputFile[] = "yaml_stream_example.yaml";
255*ec63e07aSXin Li constexpr char kOutputToRead[] = "tests_output/yaml_stream_example.yaml";
256*ec63e07aSXin Li constexpr char kOutputToExpect[] =
257*ec63e07aSXin Li "tests_expected_output/yaml_stream_example.yaml";
258*ec63e07aSXin Li
259*ec63e07aSXin Li ReadInput(kInputFile);
260*ec63e07aSXin Li EvaluateJsonnetCode(kYamlStream, true);
261*ec63e07aSXin Li WriteOutput(kOutputFile, kYamlStream);
262*ec63e07aSXin Li
263*ec63e07aSXin Li const std::string produced_output = ReadOutput(kOutputToRead);
264*ec63e07aSXin Li const std::string expected_output = ReadOutput(kOutputToExpect);
265*ec63e07aSXin Li
266*ec63e07aSXin Li ASSERT_STREQ(produced_output.c_str(), expected_output.c_str());
267*ec63e07aSXin Li }
268*ec63e07aSXin Li
269*ec63e07aSXin Li // One file depended on some other files not accessible by the sandbox
TEST_F(JsonnetTest,BadEvaluation)270*ec63e07aSXin Li TEST_F(JsonnetTest, BadEvaluation) {
271*ec63e07aSXin Li constexpr char kInputFile[] = "imports.jsonnet";
272*ec63e07aSXin Li
273*ec63e07aSXin Li ReadInput(kInputFile);
274*ec63e07aSXin Li EvaluateJsonnetCode(kBase, false);
275*ec63e07aSXin Li }
276*ec63e07aSXin Li
277*ec63e07aSXin Li } // namespace
278