1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "extensions/openpower-pels/fru_identity.hpp"
17 
18 #include <gtest/gtest.h>
19 
20 using namespace openpower::pels;
21 using namespace openpower::pels::src;
22 
23 // Unflatten a FRUIdentity that is a HW FRU callout
24 TEST(FRUIdentityTest, TestHardwareFRU)
25 {
26     // Has PN, SN, CCIN
27     std::vector<uint8_t> data{'I', 'D', 0x1C, 0x1D, // type, size, flags
28                               '1', '2', '3',  '4',  // PN
29                               '5', '6', '7',  0x00, 'A', 'A', 'A', 'A', // CCIN
30                               '1', '2', '3',  '4',  '5', '6', '7', '8', // SN
31                               '9', 'A', 'B',  'C'};
32 
33     Stream stream{data};
34 
35     FRUIdentity fru{stream};
36 
37     EXPECT_EQ(fru.failingComponentType(), FRUIdentity::hardwareFRU);
38     EXPECT_EQ(fru.flattenedSize(), data.size());
39     EXPECT_EQ(fru.type(), 0x4944);
40 
41     EXPECT_EQ(fru.getPN().value(), "1234567");
42     EXPECT_EQ(fru.getCCIN().value(), "AAAA");
43     EXPECT_EQ(fru.getSN().value(), "123456789ABC");
44     EXPECT_FALSE(fru.getMaintProc());
45 
46     // Flatten
47     std::vector<uint8_t> newData;
48     Stream newStream{newData};
49     fru.flatten(newStream);
50     EXPECT_EQ(data, newData);
51 }
52 
53 // Unflatten a FRUIdentity that is a Maintenance Procedure callout
54 TEST(FRUIdentityTest, TestMaintProcedure)
55 {
56     // Only contains the maintenance procedure
57     std::vector<uint8_t> data{
58         0x49, 0x44, 0x0C, 0x42,                     // type, size, flags
59         '1',  '2',  '3',  '4',  '5', '6', '7', 0x00 // Procedure
60     };
61 
62     Stream stream{data};
63 
64     FRUIdentity fru{stream};
65 
66     EXPECT_EQ(fru.failingComponentType(), FRUIdentity::maintenanceProc);
67     EXPECT_EQ(fru.flattenedSize(), data.size());
68 
69     EXPECT_EQ(fru.getMaintProc().value(), "1234567");
70     EXPECT_FALSE(fru.getPN());
71     EXPECT_FALSE(fru.getCCIN());
72     EXPECT_FALSE(fru.getSN());
73 
74     // Flatten
75     std::vector<uint8_t> newData;
76     Stream newStream{newData};
77     fru.flatten(newStream);
78     EXPECT_EQ(data, newData);
79 }
80 
81 // Try to unflatten garbage data
82 TEST(FRUIdentityTest, BadDataTest)
83 {
84     std::vector<uint8_t> data{0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
85                               0xFF, 0xFF, 0xFF, 0xFF};
86 
87     Stream stream{data};
88 
89     EXPECT_THROW(FRUIdentity fru{stream}, std::out_of_range);
90 }
91 
92 void testHWCallout(const std::string& pn, const std::string ccin,
93                    const std::string& sn, const std::string& expectedPN,
94                    const std::string& expectedCCIN,
95                    const std::string& expectedSN)
96 {
97     FRUIdentity fru{pn, ccin, sn};
98 
99     EXPECT_EQ(fru.flattenedSize(), 28);
100     EXPECT_EQ(fru.type(), 0x4944);
101     EXPECT_EQ(fru.failingComponentType(), FRUIdentity::hardwareFRU);
102     EXPECT_EQ(fru.getPN().value(), expectedPN);
103     EXPECT_EQ(fru.getCCIN().value(), expectedCCIN);
104     EXPECT_EQ(fru.getSN().value(), expectedSN);
105     EXPECT_FALSE(fru.getMaintProc());
106 
107     // Flatten and unflatten, then compare again
108     std::vector<uint8_t> data;
109     Stream stream{data};
110     fru.flatten(stream);
111 
112     EXPECT_EQ(data.size(), fru.flattenedSize());
113 
114     stream.offset(0);
115     FRUIdentity newFRU{stream};
116 
117     EXPECT_EQ(newFRU.flattenedSize(), fru.flattenedSize());
118     EXPECT_EQ(newFRU.type(), fru.type());
119     EXPECT_EQ(newFRU.failingComponentType(), fru.failingComponentType());
120     EXPECT_EQ(newFRU.getPN().value(), fru.getPN().value());
121     EXPECT_EQ(newFRU.getCCIN().value(), fru.getCCIN().value());
122     EXPECT_EQ(newFRU.getSN().value(), fru.getSN().value());
123     EXPECT_FALSE(newFRU.getMaintProc());
124 }
125 
126 // Test the constructor that takes in a PN/SN/CCIN
127 TEST(FRUIdentityTest, CreateHardwareCalloutTest)
128 {
129     // The right sizes
130     testHWCallout("1234567", "1234", "123456789ABC",
131                   // expected
132                   "1234567", "1234", "123456789ABC");
133 
134     // Too long
135     testHWCallout("1234567long", "1234long", "123456789ABClong",
136                   // expected
137                   "1234567", "1234", "123456789ABC");
138     // Too short
139     testHWCallout("11", "22", "333",
140                   // expected
141                   "11", "22", "333");
142 
143     // empty
144     testHWCallout("", "", "",
145                   // expected
146                   "", "", "");
147 
148     // Leading spaces in the part number will be stripped
149     testHWCallout("    567", "1234", "123456789ABC",
150                   // expected
151                   "567", "1234", "123456789ABC");
152 
153     // All spaces in the part number
154     testHWCallout("       ", "1234", "123456789ABC",
155                   // expected
156                   "", "1234", "123456789ABC");
157 }
158 
159 // Test the constructor that takes in a maint procedure
160 TEST(FRUIdentityTest, CreateProcedureCalloutTest)
161 {
162     {
163         FRUIdentity fru{"bmc_code"};
164 
165         EXPECT_EQ(fru.flattenedSize(), 12);
166         EXPECT_EQ(fru.type(), 0x4944);
167         EXPECT_EQ(fru.failingComponentType(), FRUIdentity::maintenanceProc);
168         EXPECT_EQ(fru.getMaintProc().value(), "BMC0001");
169         EXPECT_FALSE(fru.getPN());
170         EXPECT_FALSE(fru.getCCIN());
171         EXPECT_FALSE(fru.getSN());
172 
173         // Flatten and unflatten, then compare again
174         std::vector<uint8_t> data;
175         Stream stream{data};
176         fru.flatten(stream);
177 
178         EXPECT_EQ(data.size(), fru.flattenedSize());
179 
180         stream.offset(0);
181         FRUIdentity newFRU{stream};
182 
183         EXPECT_EQ(newFRU.flattenedSize(), 12);
184         EXPECT_EQ(newFRU.type(), 0x4944);
185         EXPECT_EQ(newFRU.failingComponentType(), FRUIdentity::maintenanceProc);
186         EXPECT_EQ(newFRU.getMaintProc().value(), "BMC0001");
187         EXPECT_FALSE(newFRU.getPN());
188         EXPECT_FALSE(newFRU.getCCIN());
189         EXPECT_FALSE(newFRU.getSN());
190     }
191 
192     {
193         // Invalid maintenance procedure
194         FRUIdentity fru{"invalid"};
195 
196         EXPECT_EQ(fru.flattenedSize(), 12);
197         EXPECT_EQ(fru.type(), 0x4944);
198         EXPECT_EQ(fru.failingComponentType(), FRUIdentity::maintenanceProc);
199         EXPECT_EQ(fru.getMaintProc().value(), "INVALID");
200         EXPECT_FALSE(fru.getPN());
201         EXPECT_FALSE(fru.getCCIN());
202         EXPECT_FALSE(fru.getSN());
203     }
204 
205     {
206         // Raw maintenance procedure
207         FRUIdentity fru{"BMCXXXXLONG", CalloutValueType::raw};
208         EXPECT_EQ(fru.getMaintProc().value(), "BMCXXXX");
209     }
210 }
211 
212 // Test the constructor that takes in a symbolic FRU.
213 TEST(FRUIdentityTest, CreateSymbolicFRUCalloutTest)
214 {
215     // Symbolic FRU (not trusted)
216     {
217         FRUIdentity fru{"service_docs", false};
218 
219         EXPECT_EQ(fru.flattenedSize(), 12);
220         EXPECT_EQ(fru.type(), 0x4944);
221         EXPECT_EQ(fru.failingComponentType(), FRUIdentity::symbolicFRU);
222         EXPECT_EQ(fru.getPN().value(), "SVCDOCS");
223         EXPECT_FALSE(fru.getMaintProc());
224         EXPECT_FALSE(fru.getCCIN());
225         EXPECT_FALSE(fru.getSN());
226 
227         // Flatten and unflatten, then compare again
228         std::vector<uint8_t> data;
229         Stream stream{data};
230         fru.flatten(stream);
231 
232         EXPECT_EQ(data.size(), fru.flattenedSize());
233 
234         stream.offset(0);
235         FRUIdentity newFRU{stream};
236 
237         EXPECT_EQ(newFRU.flattenedSize(), 12);
238         EXPECT_EQ(newFRU.type(), 0x4944);
239         EXPECT_EQ(newFRU.failingComponentType(), FRUIdentity::symbolicFRU);
240         EXPECT_EQ(newFRU.getPN().value(), "SVCDOCS");
241         EXPECT_FALSE(newFRU.getMaintProc());
242         EXPECT_FALSE(newFRU.getCCIN());
243         EXPECT_FALSE(newFRU.getSN());
244     }
245 
246     // Trusted symbolic FRU
247     {
248         FRUIdentity fru{"service_docs", true};
249 
250         EXPECT_EQ(fru.flattenedSize(), 12);
251         EXPECT_EQ(fru.type(), 0x4944);
252         EXPECT_EQ(fru.failingComponentType(),
253                   FRUIdentity::symbolicFRUTrustedLocCode);
254         EXPECT_EQ(fru.getPN().value(), "SVCDOCS");
255         EXPECT_FALSE(fru.getMaintProc());
256         EXPECT_FALSE(fru.getCCIN());
257         EXPECT_FALSE(fru.getSN());
258     }
259 
260     // Invalid symbolic FRU
261     {
262         FRUIdentity fru{"garbage", false};
263 
264         EXPECT_EQ(fru.flattenedSize(), 12);
265         EXPECT_EQ(fru.type(), 0x4944);
266         EXPECT_EQ(fru.failingComponentType(), FRUIdentity::symbolicFRU);
267         EXPECT_EQ(fru.getPN().value(), "INVALID");
268         EXPECT_FALSE(fru.getMaintProc());
269         EXPECT_FALSE(fru.getCCIN());
270         EXPECT_FALSE(fru.getSN());
271     }
272 
273     // Raw symbolic FRU
274     {
275         FRUIdentity fru{"SOMEFRULONG", CalloutValueType::raw, false};
276 
277         EXPECT_EQ(fru.getPN().value(), "SOMEFRU");
278     }
279 }
280