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