1 // Copyright 2024 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 //      http://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 "bios_setting.hpp"
16 #include "commands.hpp"
17 #include "helper.hpp"
18 
19 #include <stdplus/gtest/tmp.hpp>
20 
21 #include <fstream>
22 #include <ios>
23 #include <iostream>
24 #include <string>
25 #include <vector>
26 
27 #include <gtest/gtest.h>
28 
29 namespace google
30 {
31 namespace ipmi
32 {
33 
34 using testing::_;
35 using ::testing::ElementsAre;
36 
37 class BiosSettingTest : public stdplus::gtest::TestWithTmp
38 {
39   public:
40     std::string filename = std::format("{}/oem_bios_setting", CaseTmpDir());
41 
writeTmpFile(std::vector<uint8_t> payload)42     void writeTmpFile(std::vector<uint8_t> payload)
43     {
44         std::ofstream ofs;
45         ofs.open(filename, std::ios::trunc | std::ios::binary);
46         ofs.write(reinterpret_cast<char*>(payload.data()), payload.size());
47         ofs.close();
48     }
49 };
50 
TEST_F(BiosSettingTest,NoOrEmptyFileRead)51 TEST_F(BiosSettingTest, NoOrEmptyFileRead)
52 {
53     std::vector<uint8_t> request = {};
54 
55     HandlerMock hMock;
56     EXPECT_EQ(::ipmi::responseRetBytesUnavailable(),
57               readBiosSetting(request, &hMock));
58 
59     // Create an empty file
60     writeTmpFile({});
61     EXPECT_EQ(::ipmi::responseRetBytesUnavailable(),
62               readBiosSetting(request, &hMock, filename));
63     std::remove(filename.c_str());
64 }
65 
TEST_F(BiosSettingTest,SuccessfulRead)66 TEST_F(BiosSettingTest, SuccessfulRead)
67 {
68     std::vector<uint8_t> request = {};
69     // Ensure 0x0A which is a new line character '\n', is read properly
70     std::vector<uint8_t> payload = {0x0A, 0xDE, 0xAD, 0xBE, 0xEF, 0x0A};
71     std::vector<uint8_t> expectedReply = {6,    0x0A, 0xDE, 0xAD,
72                                           0xBE, 0xEF, 0x0A};
73 
74     writeTmpFile(payload);
75 
76     HandlerMock hMock;
77     auto reply = readBiosSetting(request, &hMock, filename);
78     auto result = ValidateReply(reply);
79     auto& data = result.second;
80 
81     EXPECT_EQ(SysOEMCommands::SysReadBiosSetting, result.first);
82     EXPECT_EQ(expectedReply.size() - 1, data.front());
83     EXPECT_EQ(expectedReply, data);
84     std::remove(filename.c_str());
85 }
86 
TEST_F(BiosSettingTest,InvalidRequestWrite)87 TEST_F(BiosSettingTest, InvalidRequestWrite)
88 {
89     // Empty request
90     std::vector<uint8_t> request = {};
91 
92     HandlerMock hMock;
93     EXPECT_EQ(::ipmi::responseReqDataLenInvalid(),
94               writeBiosSetting(request, &hMock));
95 
96     // Request with payload size 1 but no payload
97     request = {0x01};
98     EXPECT_EQ(::ipmi::responseReqDataLenInvalid(),
99               writeBiosSetting(request, &hMock));
100 
101     // Request with payload size 1 but actual payload size of 2 bytes
102     request = {0x01, 0x02, 0x03};
103     EXPECT_EQ(::ipmi::responseReqDataLenInvalid(),
104               writeBiosSetting(request, &hMock));
105 
106     // Request with payload size 2 but actual payload of 1 byte
107     request = {0x02, 0x02};
108     EXPECT_EQ(::ipmi::responseReqDataLenInvalid(),
109               writeBiosSetting(request, &hMock));
110 }
111 
TEST_F(BiosSettingTest,SuccessfulWrite)112 TEST_F(BiosSettingTest, SuccessfulWrite)
113 {
114     std::vector<uint8_t> request = {0x02, 0xDE, 0xAD};
115 
116     // Write a dummy file to get around permission issues with CI
117     // (Not needed in local CI)
118     writeTmpFile({});
119     HandlerMock hMock;
120     auto reply = writeBiosSetting(request, &hMock, filename);
121     auto result = ValidateReply(reply);
122     auto& data = result.second;
123 
124     EXPECT_EQ(SysOEMCommands::SysWriteBiosSetting, result.first);
125     EXPECT_EQ(std::vector<uint8_t>{2}, data);
126 
127     // Validate the payload is correct
128     reply = readBiosSetting(request, &hMock, filename);
129     result = ValidateReply(reply);
130     data = result.second;
131 
132     EXPECT_EQ(SysOEMCommands::SysReadBiosSetting, result.first);
133     EXPECT_EQ(request.size() - 1, data.front());
134     EXPECT_EQ(request, data);
135 
136     // Verify that we can write a shorter string and it'll replace the original
137     // content of the file
138     request = {0x01, 0x0A};
139 
140     reply = writeBiosSetting(request, &hMock, filename);
141     result = ValidateReply(reply);
142     data = result.second;
143 
144     EXPECT_EQ(SysOEMCommands::SysWriteBiosSetting, result.first);
145     EXPECT_EQ(std::vector<uint8_t>{1}, data);
146 
147     // Validate the payload is correct
148     reply = readBiosSetting(request, &hMock, filename);
149     result = ValidateReply(reply);
150     data = result.second;
151 
152     EXPECT_EQ(SysOEMCommands::SysReadBiosSetting, result.first);
153     EXPECT_EQ(request.size() - 1, data.front());
154     EXPECT_EQ(request, data);
155     // Cleanup the settings file
156     std::remove(filename.c_str());
157 }
158 
159 } // namespace ipmi
160 } // namespace google
161