1 #include "ipmi.hpp"
2 #include "manager_mock.hpp"
3 
4 #include <cstring>
5 #include <string>
6 
7 #include <gtest/gtest.h>
8 
9 namespace blobs
10 {
11 
12 using ::testing::_;
13 using ::testing::Invoke;
14 using ::testing::Matcher;
15 using ::testing::NotNull;
16 using ::testing::Return;
17 using ::testing::StrEq;
18 
19 // ipmid.hpp isn't installed where we can grab it and this value is per BMC
20 // SoC.
21 #define MAX_IPMI_BUFFER 64
22 
23 TEST(BlobStatTest, InvalidRequestLengthReturnsFailure)
24 {
25     // There is a minimum blobId length of one character, this test verifies
26     // we check that.
27 
28     ManagerMock mgr;
29     size_t dataLen;
30     uint8_t request[MAX_IPMI_BUFFER] = {0};
31     uint8_t reply[MAX_IPMI_BUFFER] = {0};
32     auto req = reinterpret_cast<struct BmcBlobStatTx*>(request);
33     std::string blobId = "abc";
34 
35     req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobStat);
36     req->crc = 0;
37     // length() doesn't include the nul-terminator.
38     std::memcpy(req + 1, blobId.c_str(), blobId.length());
39 
40     dataLen = sizeof(struct BmcBlobStatTx) + blobId.length();
41 
42     EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID,
43               statBlob(&mgr, request, reply, &dataLen));
44 }
45 
46 TEST(BlobStatTest, RequestRejectedReturnsFailure)
47 {
48     // The blobId is rejected for any reason.
49 
50     ManagerMock mgr;
51     size_t dataLen;
52     uint8_t request[MAX_IPMI_BUFFER] = {0};
53     uint8_t reply[MAX_IPMI_BUFFER] = {0};
54     auto req = reinterpret_cast<struct BmcBlobStatTx*>(request);
55     std::string blobId = "a";
56 
57     req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobStat);
58     req->crc = 0;
59     // length() doesn't include the nul-terminator, request buff is initialized
60     // to 0s
61     std::memcpy(req + 1, blobId.c_str(), blobId.length());
62 
63     dataLen = sizeof(struct BmcBlobStatTx) + blobId.length() + 1;
64 
65     EXPECT_CALL(mgr, stat(Matcher<const std::string&>(StrEq(blobId)),
66                           Matcher<BlobMeta*>(_)))
67         .WillOnce(Return(false));
68 
69     EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
70               statBlob(&mgr, request, reply, &dataLen));
71 }
72 
73 TEST(BlobStatTest, RequestSucceedsNoMetadata)
74 {
75     // Stat request succeeeds but there were no metadata bytes.
76 
77     ManagerMock mgr;
78     size_t dataLen;
79     uint8_t request[MAX_IPMI_BUFFER] = {0};
80     uint8_t reply[MAX_IPMI_BUFFER] = {0};
81     auto req = reinterpret_cast<struct BmcBlobStatTx*>(request);
82     std::string blobId = "a";
83 
84     req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobStat);
85     req->crc = 0;
86     // length() doesn't include the nul-terminator, request buff is initialized
87     // to 0s
88     std::memcpy(req + 1, blobId.c_str(), blobId.length());
89 
90     dataLen = sizeof(struct BmcBlobStatTx) + blobId.length() + 1;
91 
92     struct BmcBlobStatRx rep;
93     rep.crc = 0x00;
94     rep.blobState = 0x01;
95     rep.size = 0x100;
96     rep.metadataLen = 0x00;
97 
98     uint16_t blobState = rep.blobState;
99     uint32_t size = rep.size;
100 
101     EXPECT_CALL(mgr, stat(Matcher<const std::string&>(StrEq(blobId)),
102                           Matcher<BlobMeta*>(NotNull())))
103         .WillOnce(Invoke([&](const std::string&, BlobMeta* meta) {
104             meta->blobState = blobState;
105             meta->size = size;
106             return true;
107         }));
108 
109     EXPECT_EQ(IPMI_CC_OK, statBlob(&mgr, request, reply, &dataLen));
110 
111     EXPECT_EQ(sizeof(rep), dataLen);
112     EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
113 }
114 
115 TEST(BlobStatTest, RequestSucceedsWithMetadata)
116 {
117     // Stat request succeeds and there were metadata bytes.
118 
119     ManagerMock mgr;
120     size_t dataLen;
121     uint8_t request[MAX_IPMI_BUFFER] = {0};
122     uint8_t reply[MAX_IPMI_BUFFER] = {0};
123     auto req = reinterpret_cast<struct BmcBlobStatTx*>(request);
124     std::string blobId = "a";
125 
126     req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobStat);
127     req->crc = 0;
128     // length() doesn't include the nul-terminator, request buff is initialized
129     // to 0s
130     std::memcpy(req + 1, blobId.c_str(), blobId.length());
131 
132     dataLen = sizeof(struct BmcBlobStatTx) + blobId.length() + 1;
133 
134     BlobMeta lmeta;
135     lmeta.blobState = 0x01;
136     lmeta.size = 0x100;
137     lmeta.metadata.push_back(0x01);
138     lmeta.metadata.push_back(0x02);
139     lmeta.metadata.push_back(0x03);
140     lmeta.metadata.push_back(0x04);
141 
142     struct BmcBlobStatRx rep;
143     rep.crc = 0x00;
144     rep.blobState = lmeta.blobState;
145     rep.size = lmeta.size;
146     rep.metadataLen = lmeta.metadata.size();
147 
148     EXPECT_CALL(mgr, stat(Matcher<const std::string&>(StrEq(blobId)),
149                           Matcher<BlobMeta*>(NotNull())))
150         .WillOnce(Invoke([&](const std::string&, BlobMeta* meta) {
151             (*meta) = lmeta;
152             return true;
153         }));
154 
155     EXPECT_EQ(IPMI_CC_OK, statBlob(&mgr, request, reply, &dataLen));
156 
157     EXPECT_EQ(sizeof(rep) + lmeta.metadata.size(), dataLen);
158     EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
159     EXPECT_EQ(0, std::memcmp(reply + sizeof(rep), lmeta.metadata.data(),
160                              lmeta.metadata.size()));
161 }
162 } // namespace blobs
163