Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 1 | #include "libpldmresponder/pdr.hpp" |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 2 | #include "libpldmresponder/pdr_utils.hpp" |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 3 | #include "libpldmresponder/platform.hpp" |
George Liu | 1e44c73 | 2020-02-28 20:20:06 +0800 | [diff] [blame] | 4 | #include "mocked_utils.hpp" |
| 5 | #include "utils.hpp" |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 6 | |
| 7 | #include <iostream> |
| 8 | |
George Liu | 1e44c73 | 2020-02-28 20:20:06 +0800 | [diff] [blame] | 9 | using namespace pldm::utils; |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 10 | using namespace pldm::responder; |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 11 | using namespace pldm::responder::platform; |
| 12 | using namespace pldm::responder::pdr; |
| 13 | using namespace pldm::responder::pdr_utils; |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 14 | |
| 15 | TEST(getPDR, testGoodPath) |
| 16 | { |
| 17 | std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES> |
| 18 | requestPayload{}; |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 19 | auto req = reinterpret_cast<pldm_msg*>(requestPayload.data()); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 20 | size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| 21 | |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 22 | struct pldm_get_pdr_req* request = |
| 23 | reinterpret_cast<struct pldm_get_pdr_req*>(req->payload); |
| 24 | request->request_count = 100; |
| 25 | |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 26 | auto pdrRepo = pldm_pdr_init(); |
| 27 | Handler handler("./pdr_jsons/state_effecter/good", pdrRepo); |
| 28 | Repo repo(pdrRepo); |
| 29 | ASSERT_EQ(repo.empty(), false); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 30 | auto response = handler.getPDR(req, requestPayloadLength); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 31 | auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| 32 | |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 33 | struct pldm_get_pdr_resp* resp = |
| 34 | reinterpret_cast<struct pldm_get_pdr_resp*>(responsePtr->payload); |
| 35 | ASSERT_EQ(PLDM_SUCCESS, resp->completion_code); |
| 36 | ASSERT_EQ(2, resp->next_record_handle); |
| 37 | ASSERT_EQ(true, resp->response_count != 0); |
| 38 | |
| 39 | pldm_pdr_hdr* hdr = reinterpret_cast<pldm_pdr_hdr*>(resp->record_data); |
| 40 | ASSERT_EQ(hdr->record_handle, 1); |
| 41 | ASSERT_EQ(hdr->version, 1); |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 42 | |
| 43 | pldm_pdr_destroy(pdrRepo); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 44 | } |
| 45 | |
| 46 | TEST(getPDR, testShortRead) |
| 47 | { |
| 48 | std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES> |
| 49 | requestPayload{}; |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 50 | auto req = reinterpret_cast<pldm_msg*>(requestPayload.data()); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 51 | size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| 52 | |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 53 | struct pldm_get_pdr_req* request = |
| 54 | reinterpret_cast<struct pldm_get_pdr_req*>(req->payload); |
| 55 | request->request_count = 1; |
| 56 | |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 57 | auto pdrRepo = pldm_pdr_init(); |
| 58 | Handler handler("./pdr_jsons/state_effecter/good", pdrRepo); |
| 59 | Repo repo(pdrRepo); |
| 60 | ASSERT_EQ(repo.empty(), false); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 61 | auto response = handler.getPDR(req, requestPayloadLength); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 62 | auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 63 | struct pldm_get_pdr_resp* resp = |
| 64 | reinterpret_cast<struct pldm_get_pdr_resp*>(responsePtr->payload); |
| 65 | ASSERT_EQ(PLDM_SUCCESS, resp->completion_code); |
| 66 | ASSERT_EQ(1, resp->response_count); |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 67 | pldm_pdr_destroy(pdrRepo); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | TEST(getPDR, testBadRecordHandle) |
| 71 | { |
| 72 | std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES> |
| 73 | requestPayload{}; |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 74 | auto req = reinterpret_cast<pldm_msg*>(requestPayload.data()); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 75 | size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| 76 | |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 77 | struct pldm_get_pdr_req* request = |
| 78 | reinterpret_cast<struct pldm_get_pdr_req*>(req->payload); |
| 79 | request->record_handle = 100000; |
| 80 | request->request_count = 1; |
| 81 | |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 82 | auto pdrRepo = pldm_pdr_init(); |
| 83 | Handler handler("./pdr_jsons/state_effecter/good", pdrRepo); |
| 84 | Repo repo(pdrRepo); |
| 85 | ASSERT_EQ(repo.empty(), false); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 86 | auto response = handler.getPDR(req, requestPayloadLength); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 87 | auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
| 88 | |
| 89 | ASSERT_EQ(responsePtr->payload[0], PLDM_PLATFORM_INVALID_RECORD_HANDLE); |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 90 | |
| 91 | pldm_pdr_destroy(pdrRepo); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | TEST(getPDR, testNoNextRecord) |
| 95 | { |
| 96 | std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES> |
| 97 | requestPayload{}; |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 98 | auto req = reinterpret_cast<pldm_msg*>(requestPayload.data()); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 99 | size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| 100 | |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 101 | struct pldm_get_pdr_req* request = |
| 102 | reinterpret_cast<struct pldm_get_pdr_req*>(req->payload); |
| 103 | request->record_handle = 1; |
| 104 | |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 105 | auto pdrRepo = pldm_pdr_init(); |
| 106 | Handler handler("./pdr_jsons/state_effecter/good", pdrRepo); |
| 107 | Repo repo(pdrRepo); |
| 108 | ASSERT_EQ(repo.empty(), false); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 109 | auto response = handler.getPDR(req, requestPayloadLength); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 110 | auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 111 | struct pldm_get_pdr_resp* resp = |
| 112 | reinterpret_cast<struct pldm_get_pdr_resp*>(responsePtr->payload); |
| 113 | ASSERT_EQ(PLDM_SUCCESS, resp->completion_code); |
| 114 | ASSERT_EQ(2, resp->next_record_handle); |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 115 | |
| 116 | pldm_pdr_destroy(pdrRepo); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 117 | } |
| 118 | |
| 119 | TEST(getPDR, testFindPDR) |
| 120 | { |
| 121 | std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES> |
| 122 | requestPayload{}; |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 123 | auto req = reinterpret_cast<pldm_msg*>(requestPayload.data()); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 124 | size_t requestPayloadLength = requestPayload.size() - sizeof(pldm_msg_hdr); |
| 125 | |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 126 | struct pldm_get_pdr_req* request = |
| 127 | reinterpret_cast<struct pldm_get_pdr_req*>(req->payload); |
| 128 | request->request_count = 100; |
| 129 | |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 130 | auto pdrRepo = pldm_pdr_init(); |
| 131 | Handler handler("./pdr_jsons/state_effecter/good", pdrRepo); |
| 132 | Repo repo(pdrRepo); |
| 133 | ASSERT_EQ(repo.empty(), false); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 134 | auto response = handler.getPDR(req, requestPayloadLength); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 135 | |
| 136 | // Let's try to find a PDR of type stateEffecter (= 11) and entity type = |
| 137 | // 100 |
| 138 | bool found = false; |
| 139 | uint32_t handle = 0; // start asking for PDRs from recordHandle 0 |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 140 | while (!found) |
| 141 | { |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 142 | request->record_handle = handle; |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 143 | auto response = handler.getPDR(req, requestPayloadLength); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 144 | auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 145 | struct pldm_get_pdr_resp* resp = |
| 146 | reinterpret_cast<struct pldm_get_pdr_resp*>(responsePtr->payload); |
| 147 | ASSERT_EQ(PLDM_SUCCESS, resp->completion_code); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 148 | |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 149 | handle = resp->next_record_handle; // point to the next pdr in case |
| 150 | // current is not what we want |
| 151 | |
| 152 | pldm_pdr_hdr* hdr = reinterpret_cast<pldm_pdr_hdr*>(resp->record_data); |
Sampa Misra | aa8ae72 | 2019-12-12 03:20:40 -0600 | [diff] [blame] | 153 | std::cerr << "PDR next record handle " << handle << "\n"; |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 154 | std::cerr << "PDR type " << hdr->type << "\n"; |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 155 | if (hdr->type == PLDM_STATE_EFFECTER_PDR) |
| 156 | { |
| 157 | pldm_state_effecter_pdr* pdr = |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 158 | reinterpret_cast<pldm_state_effecter_pdr*>(resp->record_data); |
Sampa Misra | aa8ae72 | 2019-12-12 03:20:40 -0600 | [diff] [blame] | 159 | std::cerr << "PDR entity type " << pdr->entity_type << "\n"; |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 160 | if (pdr->entity_type == 100) |
| 161 | { |
| 162 | found = true; |
| 163 | // Rest of the PDR can be accessed as need be |
| 164 | break; |
| 165 | } |
| 166 | } |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 167 | if (!resp->next_record_handle) // no more records |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 168 | { |
| 169 | break; |
| 170 | } |
| 171 | } |
| 172 | ASSERT_EQ(found, true); |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 173 | |
| 174 | pldm_pdr_destroy(pdrRepo); |
Deepak Kodihalli | 557dfb0 | 2019-05-12 13:11:17 +0530 | [diff] [blame] | 175 | } |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 176 | |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 177 | TEST(setStateEffecterStatesHandler, testGoodRequest) |
| 178 | { |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 179 | auto inPDRRepo = pldm_pdr_init(); |
| 180 | auto outPDRRepo = pldm_pdr_init(); |
| 181 | Repo outRepo(outPDRRepo); |
| 182 | Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo); |
| 183 | Repo inRepo(inPDRRepo); |
| 184 | getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 185 | pdr_utils::PdrEntry e; |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 186 | auto record1 = pdr::getRecordByHandle(outRepo, 1, e); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 187 | ASSERT_NE(record1, nullptr); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 188 | pldm_state_effecter_pdr* pdr = |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 189 | reinterpret_cast<pldm_state_effecter_pdr*>(e.data); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 190 | EXPECT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR); |
| 191 | |
| 192 | std::vector<set_effecter_state_field> stateField; |
| 193 | stateField.push_back({PLDM_REQUEST_SET, 1}); |
| 194 | stateField.push_back({PLDM_REQUEST_SET, 1}); |
George Liu | 1ec85d4 | 2020-02-12 16:05:32 +0800 | [diff] [blame] | 195 | std::string value = "xyz.openbmc_project.Foo.Bar.V1"; |
George Liu | 1e44c73 | 2020-02-28 20:20:06 +0800 | [diff] [blame] | 196 | PropertyValue propertyValue = value; |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 197 | |
| 198 | MockdBusHandler handlerObj; |
George Liu | 1ec85d4 | 2020-02-12 16:05:32 +0800 | [diff] [blame] | 199 | DBusMapping dbusMapping{"/foo/bar", "xyz.openbmc_project.Foo.Bar", |
| 200 | "propertyName", "string"}; |
George Liu | 1e44c73 | 2020-02-28 20:20:06 +0800 | [diff] [blame] | 201 | |
| 202 | EXPECT_CALL(handlerObj, setDbusProperty(dbusMapping, propertyValue)) |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 203 | .Times(2); |
Deepak Kodihalli | bc669f1 | 2019-11-28 08:52:07 -0600 | [diff] [blame] | 204 | auto rc = handler.setStateEffecterStatesHandler<MockdBusHandler>( |
| 205 | handlerObj, 0x1, stateField); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 206 | ASSERT_EQ(rc, 0); |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 207 | |
| 208 | pldm_pdr_destroy(inPDRRepo); |
| 209 | pldm_pdr_destroy(outPDRRepo); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | TEST(setStateEffecterStatesHandler, testBadRequest) |
| 213 | { |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 214 | auto inPDRRepo = pldm_pdr_init(); |
| 215 | auto outPDRRepo = pldm_pdr_init(); |
| 216 | Repo outRepo(outPDRRepo); |
| 217 | Handler handler("./pdr_jsons/state_effecter/good", inPDRRepo); |
| 218 | Repo inRepo(inPDRRepo); |
| 219 | getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 220 | pdr_utils::PdrEntry e; |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 221 | auto record1 = pdr::getRecordByHandle(outRepo, 1, e); |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 222 | ASSERT_NE(record1, nullptr); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 223 | pldm_state_effecter_pdr* pdr = |
George Liu | e53193f | 2020-02-24 09:23:26 +0800 | [diff] [blame] | 224 | reinterpret_cast<pldm_state_effecter_pdr*>(e.data); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 225 | EXPECT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR); |
| 226 | |
| 227 | std::vector<set_effecter_state_field> stateField; |
| 228 | stateField.push_back({PLDM_REQUEST_SET, 3}); |
| 229 | stateField.push_back({PLDM_REQUEST_SET, 4}); |
| 230 | |
| 231 | MockdBusHandler handlerObj; |
Deepak Kodihalli | bc669f1 | 2019-11-28 08:52:07 -0600 | [diff] [blame] | 232 | auto rc = handler.setStateEffecterStatesHandler<MockdBusHandler>( |
| 233 | handlerObj, 0x1, stateField); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 234 | ASSERT_EQ(rc, PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE); |
| 235 | |
Deepak Kodihalli | bc669f1 | 2019-11-28 08:52:07 -0600 | [diff] [blame] | 236 | rc = handler.setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x9, |
| 237 | stateField); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 238 | ASSERT_EQ(rc, PLDM_PLATFORM_INVALID_EFFECTER_ID); |
| 239 | |
| 240 | stateField.push_back({PLDM_REQUEST_SET, 4}); |
Deepak Kodihalli | bc669f1 | 2019-11-28 08:52:07 -0600 | [diff] [blame] | 241 | rc = handler.setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x1, |
| 242 | stateField); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 243 | ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA); |
| 244 | |
Deepak Kodihalli | c682fe2 | 2020-03-04 00:42:54 -0600 | [diff] [blame] | 245 | pldm_pdr_destroy(inPDRRepo); |
| 246 | pldm_pdr_destroy(outPDRRepo); |
Sampa Misra | a2fa070 | 2019-05-31 01:28:55 -0500 | [diff] [blame] | 247 | } |