// Copyright(c) 2017-2020, Intel Corporation // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of Intel Corporation nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. extern "C" { #include #include "sysfs_int.h" #include "types_int.h" fpga_result cat_token_sysfs_path(char *, fpga_token, const char *); fpga_result get_port_sysfs(fpga_handle, char *); fpga_result sysfs_get_socket_id(int, int, fpga_guid); fpga_result sysfs_get_afu_id(int, int, fpga_guid); fpga_result sysfs_get_pr_id(int, int, fpga_guid); fpga_result sysfs_get_slots(int, int, uint32_t *); fpga_result sysfs_get_bitstream_id(int, int, uint64_t *); fpga_result sysfs_sbdf_from_path(const char *, int *, int *, int *, int *); fpga_result opae_glob_path(char *, size_t ); fpga_result opae_glob_paths(const char *path, size_t found_max, char *found[], size_t *num_found); fpga_result make_sysfs_group(char *, const char *, fpga_object *, int, fpga_handle); ssize_t eintr_write(int, void *, size_t); char* cstr_dup(const char *str); int parse_pcie_info(sysfs_fpga_device *device, char *buffer); fpga_result sysfs_get_interface_id(fpga_token token, fpga_guid guid); sysfs_fpga_region* make_region(sysfs_fpga_device*, char*, int, fpga_objtype); int xfpga_plugin_initialize(void); int xfpga_plugin_finalize(void); fpga_result re_match_region(const char *fmt, char *inpstr, char type[], size_t, int *num); } #include #include #include #include #include #include #include #include #include "xfpga.h" #include #include "gtest/gtest.h" #include "mock/test_system.h" const std::string single_sysfs_fme = "/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme.0"; const std::string single_sysfs_port = "/sys/class/fpga/intel-fpga-dev.0/intel-fpga-port.0"; const std::string single_dev_fme = "/dev/intel-fpga-fme.0"; const std::string single_dev_port = "/dev/intel-fpga-port.0"; using namespace opae::testing; class sysfsinit_c_p : public ::testing::TestWithParam { protected: sysfsinit_c_p(){} virtual void SetUp() override { ASSERT_TRUE(test_platform::exists(GetParam())); platform_ = test_platform::get(GetParam()); system_ = test_system::instance(); system_->initialize(); system_->prepare_syfs(platform_); ASSERT_EQ(xfpga_plugin_initialize(), FPGA_OK); ASSERT_GT(sysfs_device_count(), 0); sysfs_fpga_region *fme = nullptr; sysfs_fpga_region *port = nullptr; for (int i = 0; i < sysfs_device_count(); ++i) { fme = (fme == nullptr) ? sysfs_get_device(i)->fme : fme; port = (port == nullptr) ? sysfs_get_device(i)->port : port; if (fme && port) break; } ASSERT_NE(fme, nullptr); ASSERT_NE(port, nullptr); sysfs_fme = std::string(fme->sysfs_path); dev_fme = std::string("/dev/") + std::string(fme->sysfs_name); sysfs_port = std::string(port->sysfs_path); dev_port = std::string("/dev/") + std::string(port->sysfs_name); } virtual void TearDown() override { xfpga_plugin_finalize(); system_->finalize(); } int GetNumFpgas() { if (platform_.mock_sysfs != nullptr) { return platform_.devices.size(); } int value; std::string cmd = "(ls -l /sys/class/fpga*/*/*fme*/dev || " "ls -l /sys/class/fpga*/*intel*) | (wc -l)"; ExecuteCmd(cmd, value); return value; } int GetNumMatchedFpga() { if (platform_.mock_sysfs != nullptr) { return platform_.devices.size(); } std::stringstream ss; ss << std::setw(4) << std::hex << platform_.devices[0].device_id; std::string deviceid (ss.str()); std::string cmd = "lspci | grep " + deviceid + " | wc -l"; int value; ExecuteCmd(cmd, value); return value; } void ExecuteCmd(std::string cmd, int &value) { std::string line; std::string command = cmd + " > output.txt"; EXPECT_EQ(std::system(command.c_str()), 0); std::ifstream file("output.txt"); ASSERT_TRUE(file.is_open()); EXPECT_TRUE(std::getline(file, line)); file.close(); EXPECT_EQ(std::system("rm output.txt"), 0); value = std::stoi(line); } test_platform platform_; test_system *system_; std::string sysfs_fme; std::string dev_fme; std::string sysfs_port; std::string dev_port; }; // convert segment, bus, device, function to a 32 bit number uint32_t to_uint32(uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) { return (segment << 16) | (bus << 8) | (device << 5) | (function & 7); } TEST_P(sysfsinit_c_p, sysfs_initialize) { std::map devices; // define a callback to be used with sysfs_foreach_device // this callback is given a map of devices using the sbdf as the key // (as a 32-bit number); auto cb = [](const sysfs_fpga_device *r, void* data) -> fpga_result { auto& devs = *reinterpret_cast*>(data); auto id = to_uint32(r->segment, r->bus, r->device, r->function); auto it = devs.find(id); if (it != devs.end()) { if (it->second.device_id == r->device_id && it->second.vendor_id == r->vendor_id) { devs.erase(id); } } return FPGA_OK; }; // build a map of tests devices where the key is the sbdf as a 32-bit number for (const auto &d : platform_.devices) { devices[to_uint32(d.segment, d.bus, d.device, d.function)] = d; } if (platform_.devices[0].num_vfs) { auto d = platform_.devices[0]; d.function = 1; d.device_id++; devices[to_uint32(d.segment, d.bus, d.device, 1)] = d; } auto num_vfs = platform_.devices[0].num_vfs; // the size of this map should be equal to the number of devices in our // platform ASSERT_EQ(devices.size(), platform_.devices.size() + num_vfs); EXPECT_EQ(GetNumFpgas(), sysfs_device_count() - num_vfs); // call sysfs_foreach_device with our callback, cb sysfs_foreach_device(cb, &devices); // our devices map should be empty after this call as this callback removes // entries if the device structure matches a device object in the map EXPECT_EQ(devices.size(), 0); } TEST_P(sysfsinit_c_p, sysfs_get_device) { std::map devices; // build a map of tests devices where the key is the sbdf as a 32-bit number for (const auto &d : platform_.devices) { devices[to_uint32(d.segment, d.bus, d.device, d.function)] = d; } // the size of this map should be equal to the number of devices in our // platform ASSERT_EQ(devices.size(), platform_.devices.size()); auto num_vfs = platform_.devices[0].num_vfs; EXPECT_EQ(GetNumFpgas(), sysfs_device_count() - num_vfs); // use sysfs_get_device API to count how many devices match our devices map for (int i = 0; i < sysfs_device_count(); ++i) { auto device = sysfs_get_device(i); ASSERT_NE(device, nullptr); auto id = to_uint32(device->segment, device->bus, device->device, device->function); auto it = devices.find(id); if (it != devices.end() && it->second.device_id == device->device_id && it->second.vendor_id == device->vendor_id) { devices.erase(id); } } // our devices map should be empty after the loop above EXPECT_EQ(devices.size(), 0); } /** * @test get_interface_id * @details Given a valid token When I call sysfs_get_interface_id with that token * I get the expected interface_id */ TEST_P(sysfsinit_c_p, get_interface_id) { fpga_guid guid; fpga_properties props; fpga_token fme; uint32_t matches = 0; fpga_guid parsed_guid; ASSERT_EQ(fpgaGetProperties(nullptr, &props), FPGA_OK); ASSERT_EQ(fpgaPropertiesSetDeviceID(props,platform_.devices[0].device_id), FPGA_OK); ASSERT_EQ(fpgaPropertiesSetVendorID(props,platform_.devices[0].vendor_id), FPGA_OK); ASSERT_EQ(fpgaPropertiesSetObjectType(props, FPGA_DEVICE), FPGA_OK); ASSERT_EQ(xfpga_fpgaEnumerate(&props, 1, &fme, 1, &matches), FPGA_OK); EXPECT_EQ(matches, GetNumMatchedFpga()); ASSERT_EQ(sysfs_get_interface_id(fme, guid), 0); EXPECT_EQ(uuid_parse(platform_.devices[0].fme_guid, parsed_guid), 0); EXPECT_EQ(uuid_compare(parsed_guid, guid), 0); EXPECT_EQ(xfpga_fpgaDestroyToken(&fme), FPGA_OK); EXPECT_EQ(fpgaDestroyProperties(&props), FPGA_OK); } TEST(sysfsinit_c_p, sysfs_parse_pcie) { sysfs_fpga_device device; char buffer1[] = "../../devices/pci0000:00/0000:00:02.0/0f0f:05:04.3/fpga/intel-fpga-dev.0"; char buffer2[] = "../../devices/pci0000:5e/a0a0:5e:02.1/fpga_device/device0"; auto res = parse_pcie_info(&device, buffer1); EXPECT_EQ(res, 0); EXPECT_EQ(device.segment, 0x0f0f); EXPECT_EQ(device.bus, 0x05); EXPECT_EQ(device.device, 0x04); EXPECT_EQ(device.function, 0x03); res = parse_pcie_info(&device, buffer2); EXPECT_EQ(res, 0); EXPECT_EQ(device.segment, 0xa0a0); EXPECT_EQ(device.bus, 0x5e); EXPECT_EQ(device.device, 0x02); EXPECT_EQ(device.function, 0x01); } INSTANTIATE_TEST_CASE_P(sysfsinit_c, sysfsinit_c_p, ::testing::ValuesIn(test_platform::platforms())); class sysfs_c_p : public ::testing::TestWithParam { protected: sysfs_c_p() : tokens_{{nullptr, nullptr}}, handle_(nullptr){} virtual void SetUp() override { ASSERT_TRUE(test_platform::exists(GetParam())); platform_ = test_platform::get(GetParam()); system_ = test_system::instance(); system_->initialize(); system_->prepare_syfs(platform_); ASSERT_EQ(xfpga_plugin_initialize(), FPGA_OK); ASSERT_EQ(xfpga_fpgaGetProperties(nullptr, &filter_), FPGA_OK); ASSERT_EQ(fpgaPropertiesSetDeviceID(filter_, platform_.devices[0].device_id), FPGA_OK); ASSERT_EQ(fpgaPropertiesSetObjectType(filter_, FPGA_DEVICE), FPGA_OK); ASSERT_EQ(xfpga_fpgaEnumerate(&filter_, 1, tokens_.data(), tokens_.size(), &num_matches_), FPGA_OK); ASSERT_EQ(xfpga_fpgaOpen(tokens_[0], &handle_, 0), FPGA_OK); sysfs_fpga_region *fme = nullptr; sysfs_fpga_region *port = nullptr; if (sysfs_device_count() > 0) { for (int i = 0; i < sysfs_device_count(); ++i) { fme = fme == nullptr ? sysfs_get_device(i)->fme : fme; port = port == nullptr ? sysfs_get_device(i)->port : port; } } ASSERT_NE(fme, nullptr); ASSERT_NE(port, nullptr); sysfs_fme = std::string(fme->sysfs_path); dev_fme = std::string("/dev/") + std::string(fme->sysfs_name); sysfs_port = std::string(port->sysfs_path); dev_port = std::string("/dev/") + std::string(port->sysfs_name); } virtual void TearDown() override { EXPECT_EQ(fpgaDestroyProperties(&filter_), FPGA_OK); if (handle_) { EXPECT_EQ(xfpga_fpgaClose(handle_), FPGA_OK); handle_ = nullptr; } for (auto &t : tokens_) { if (t) { EXPECT_EQ(FPGA_OK, xfpga_fpgaDestroyToken(&t)); t = nullptr; } } xfpga_plugin_finalize(); system_->finalize(); } std::array tokens_; fpga_handle handle_; fpga_properties filter_; uint32_t num_matches_; test_platform platform_; test_system *system_; std::string sysfs_fme; std::string dev_fme; std::string sysfs_port; std::string dev_port; }; /** * @test eintr_write_tests * @details Given a valid fd but invalid buffer, eintr_writes * returns -1 on error. */ TEST(sysfs_c, eintr_write_tests) { void * data = nullptr; std::string filename = "empty_file.txt"; EXPECT_EQ(std::system("touch empty_file.txt"), 0); int fd = open(filename.c_str(), O_RDWR); EXPECT_NE(fd, -1); size_t count = 1024; EXPECT_EQ(-1, eintr_write(fd, data, count)); EXPECT_EQ(close(fd), 0); EXPECT_EQ(std::system("rm empty_file.txt"), 0); } /** * @test sysfs_invalid_tests * @details When calling get_port_sysfs with invalid params * the functino returns FPGA_INVALID_PARAM */ TEST_P(sysfs_c_p, sysfs_invalid_tests) { const std::string sysfs_fme = "/sys/class/fpga/intel-fpga-dev/intel-fpga-fme"; auto h = (struct _fpga_handle *)handle_; auto t = (struct _fpga_token *)h->token; char spath[SYSFS_PATH_MAX]; fpga_result res; char invalid_string[] = "..."; strncpy(t->sysfspath, invalid_string, 4); res = get_port_sysfs(handle_, spath); EXPECT_EQ(FPGA_INVALID_PARAM, res); h->token = NULL; res = get_port_sysfs(handle_, spath); EXPECT_EQ(FPGA_INVALID_PARAM, res); } /** * @test hw_type_invalid_test * @details */ TEST_P(sysfs_c_p, hw_type_invalid_tests) { auto h = (struct _fpga_handle *)handle_; enum fpga_hw_type hw_type = FPGA_HW_UNKNOWN; fpga_token tok; auto res = get_fpga_hw_type(handle_, NULL); EXPECT_EQ(FPGA_INVALID_PARAM, res); tok = h->token; h->token = NULL; res = get_fpga_hw_type(handle_, &hw_type); EXPECT_EQ(FPGA_INVALID_PARAM, res); h->token = tok; res = get_fpga_hw_type(handle_, &hw_type); EXPECT_EQ(FPGA_OK, res); } /** * @test glob_test * @details */ TEST_P(sysfs_c_p, glob_tests) { std::string invalid_filename = "opae"; auto res = opae_glob_path(nullptr, 0); EXPECT_EQ(FPGA_EXCEPTION, res); res = opae_glob_path(const_cast(invalid_filename.c_str()), invalid_filename.length() - 1); EXPECT_EQ(FPGA_NOT_FOUND, res); } TEST_P(sysfs_c_p, glob_paths) { char *paths[16]; auto bitstream_glob = sysfs_fme + "/bitstream*"; size_t found = 0; ASSERT_EQ(opae_glob_paths(bitstream_glob.c_str(), 16, paths, &found), FPGA_OK); EXPECT_EQ(found, 2); // opae_glob_paths allocates memory for each path found // let's free it here since we don't need it any longer for (int i = 0; i < found; ++i) { free(paths[i]); } } /** * @test cat_sysfs_path_errors * @details */ TEST(sysfs_c, cat_sysfs_path_errors) { std::vector buffer(256); std::string emptystring = ""; EXPECT_EQ(FPGA_OK, cat_sysfs_path(buffer.data(), single_sysfs_port.c_str())); EXPECT_EQ(FPGA_INVALID_PARAM, cat_sysfs_path(buffer.data(), nullptr)); EXPECT_EQ(FPGA_INVALID_PARAM, cat_sysfs_path(nullptr, single_sysfs_port.c_str())); EXPECT_EQ(FPGA_INVALID_PARAM, cat_sysfs_path(nullptr, nullptr)); } /** * @test cat_token_sysfs_path * @details */ TEST(sysfs_c, cat_token_sysfs_path) { _fpga_token tok; std::copy(single_sysfs_fme.begin(), single_sysfs_fme.end(), &tok.sysfspath[0]); tok.sysfspath[single_sysfs_fme.size()] = '\0'; std::copy(single_dev_fme.begin(), single_dev_fme.end(), &tok.devpath[0]); tok.devpath[single_dev_fme.size()] = '\0'; std::vector buffer(256); EXPECT_EQ(cat_token_sysfs_path(buffer.data(), &tok, "bitstream_id"), FPGA_OK); EXPECT_STREQ(buffer.data(), std::string(single_sysfs_fme + "/bitstream_id").c_str()); // null destination EXPECT_EQ(cat_token_sysfs_path(nullptr, &tok, "bitstream_id"), FPGA_EXCEPTION); } /** * @test cat_handle_sysfs_path * @details */ TEST(sysfs_c, cat_handle_sysfs_path) { _fpga_token tok; _fpga_handle hnd; std::copy(single_sysfs_fme.begin(), single_sysfs_fme.end(), &tok.sysfspath[0]); tok.sysfspath[single_sysfs_fme.size()] = '\0'; std::copy(single_dev_fme.begin(), single_dev_fme.end(), &tok.devpath[0]); tok.devpath[single_dev_fme.size()] = '\0'; hnd.token = &tok; std::vector buffer(256); EXPECT_EQ(cat_handle_sysfs_path(buffer.data(), &hnd, "bitstream_id"), FPGA_OK); EXPECT_STREQ(buffer.data(), std::string(single_sysfs_fme + "/bitstream_id").c_str()); // null destination EXPECT_EQ(cat_handle_sysfs_path(nullptr, &hnd, "bitstream_id"), FPGA_EXCEPTION); } /** * @test make_object * @details */ TEST_P(sysfs_c_p, make_object) { _fpga_token *tok = static_cast<_fpga_token *>(tokens_[0]); fpga_object object; // errors is a sysfs directory - this should call make_sysfs_group() ASSERT_EQ(make_sysfs_object(tok->sysfspath, "errors", &object, 0, 0), FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&object), FPGA_OK); } /** * @test sysfs_sbdf_invalid_tests * @details When calling sysfs_sbdf_from path with invalid params * the function returns FPGA_NO_DRIVER */ TEST(sysfs_c, sysfs_sbdf_invalid_tests) { std::string sysfs_dev = "/sys/devices/pci0000:5e/0000:5e:00.0/fpga/intel-fpga-dev.0"; int s = 0, b = 0, d = 0, f = 0; auto res = sysfs_sbdf_from_path(sysfs_dev.c_str(), &s, &b, &d, &f); EXPECT_EQ(FPGA_NO_DRIVER, res); } /** * @test hw_type * @details get_fpga_hw_type given valid parameters * returns FPGA_OK */ TEST_P(sysfs_c_p, hw_type) { enum fpga_hw_type hw_type = FPGA_HW_UNKNOWN; uint64_t real_vendorid = platform_.devices[0].vendor_id; uint64_t real_deviceid = platform_.devices[0].device_id; auto res = get_fpga_hw_type(handle_, &hw_type); EXPECT_EQ(res, FPGA_OK); EXPECT_EQ(hw_type, opae_id_to_hw_type(real_vendorid, real_deviceid)); } /** * @test cstr_dup * @details Duplicate an input string */ TEST_P(sysfs_c_p, cstr_dup) { std::string inp("this is an input string"); char *dup = cstr_dup(inp.c_str()); EXPECT_STREQ(dup, inp.c_str()); free(dup); } /** * @test cstr_dup * @details Invalidate malloc call */ TEST_P(sysfs_c_p, cstr_dup_1) { std::string inp("this is an input string"); test_system::instance()->invalidate_malloc(); char *dup = cstr_dup(inp.c_str()); EXPECT_EQ(dup, nullptr); } /** * @test get_fme_path * @details Given a valid sysfs path to a port node * When I call sysfs_get_fme_path with the path * Then the return value is FPGA_OK * And I get a sysfs path to the fme node * And its realpath is equal to the realpath of the SUT */ TEST_P(sysfs_c_p, get_fme_path) { char found_fme[PATH_MAX]; char rpath1[PATH_MAX]; char rpath2[PATH_MAX]; ASSERT_EQ(sysfs_get_fme_path(sysfs_port.c_str(), found_fme), FPGA_OK); ASSERT_NE(realpath(sysfs_fme.c_str(), rpath1), nullptr); ASSERT_NE(realpath(found_fme, rpath2), nullptr); ASSERT_STREQ(rpath1, rpath2); } /** * @test get_fme_path_neg * @details Given an invalid sysfs path to a port node * When I call sysfs_get_fme_path with the path * Then the return value is not FPGA_OK */ TEST_P(sysfs_c_p, get_fme_path_neg) { char found_fme[PATH_MAX]; ASSERT_NE(sysfs_get_fme_path("/a/b/c", found_fme), FPGA_OK); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_c_p, ::testing::ValuesIn(test_platform::platforms({}))); class sysfs_c_hw_p : public sysfs_c_p { protected: sysfs_c_hw_p() {} }; /** * @test make_sysfs_group * @details */ TEST_P(sysfs_c_hw_p, make_sysfs) { const std::string invalid_path = "/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme"; _fpga_token *tok = static_cast<_fpga_token *>(tokens_[0]); fpga_object obj; auto res = make_sysfs_group(tok->sysfspath, "errors", &obj, 0, handle_); EXPECT_EQ(res, FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&obj), FPGA_OK); res = make_sysfs_group(tok->sysfspath, "errors", &obj, FPGA_OBJECT_GLOB, handle_); EXPECT_EQ(res, FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&obj), FPGA_OK); res = make_sysfs_group(const_cast(invalid_path.c_str()), "errors", &obj, 0, handle_); EXPECT_EQ(res, FPGA_NOT_FOUND); res = make_sysfs_group(tok->sysfspath, "errors", &obj, FPGA_OBJECT_RECURSE_ONE, handle_); EXPECT_EQ(res, FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&obj), FPGA_OK); } /** * @test make_object_glob * @details */ TEST_P(sysfs_c_hw_p, make_object_glob) { _fpga_token *tok = static_cast<_fpga_token *>(tokens_[0]); fpga_object object; // errors is a sysfs directory - this should call make_sysfs_group() ASSERT_EQ(make_sysfs_object(tok->sysfspath, "errors", &object, FPGA_OBJECT_GLOB, 0), FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&object), FPGA_OK); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_c_hw_p, ::testing::ValuesIn(test_platform::hw_platforms({ "skx-p","dcp-rc","dcp-vc" }))); class sysfs_c_mock_p : public sysfs_c_p { protected: sysfs_c_mock_p() {} }; /** * @test make_sysfs_group * @details */ TEST_P(sysfs_c_mock_p, make_sysfs) { const std::string invalid_path = "/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme"; _fpga_token *tok = static_cast<_fpga_token *>(tokens_[0]); fpga_object obj; auto res = make_sysfs_group(tok->sysfspath, "errors", &obj, 0, handle_); EXPECT_EQ(res, FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&obj), FPGA_OK); res = make_sysfs_group(tok->sysfspath, "errors", &obj, FPGA_OBJECT_GLOB, handle_); EXPECT_EQ(res, FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&obj), FPGA_OK); res = make_sysfs_group(const_cast(invalid_path.c_str()), "errors", &obj, 0, handle_); EXPECT_EQ(res, FPGA_NOT_FOUND); res = make_sysfs_group(tok->sysfspath, "errors", &obj, FPGA_OBJECT_RECURSE_ONE, handle_); EXPECT_EQ(res, FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&obj), FPGA_OK); } /** * @test make_object_glob * @details */ TEST_P(sysfs_c_mock_p, make_object_glob) { _fpga_token *tok = static_cast<_fpga_token *>(tokens_[0]); fpga_object object; // errors is a sysfs directory - this should call make_sysfs_group() ASSERT_EQ(make_sysfs_object(tok->sysfspath, "errors", &object, FPGA_OBJECT_GLOB, 0), FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&object), FPGA_OK); } TEST_P(sysfs_c_mock_p, glob_bitstream_objs) { fpga_object container, bitstream1, bitstream2; ASSERT_EQ(xfpga_fpgaTokenGetObject(tokens_[0], "bitstream*", &container, FPGA_OBJECT_GLOB), FPGA_OK); enum fpga_sysobject_type type; EXPECT_EQ(xfpga_fpgaObjectGetType(container, &type), FPGA_OK); EXPECT_EQ(type, FPGA_OBJECT_CONTAINER); uint32_t sz = 0; EXPECT_EQ(xfpga_fpgaObjectGetSize(container, &sz, 0), FPGA_OK); EXPECT_EQ(sz, 2); EXPECT_EQ(xfpga_fpgaObjectGetObjectAt(container, 0, &bitstream1), FPGA_OK); EXPECT_EQ(xfpga_fpgaObjectGetObjectAt(container, 1, &bitstream2), FPGA_OK); char name1[64] = {'\0'}; char name2[64] = {'\0'}; EXPECT_EQ(xfpga_fpgaObjectGetName(bitstream1, name1, sizeof(name1)), FPGA_OK); EXPECT_EQ(xfpga_fpgaObjectGetName(bitstream2, name2, sizeof(name2)), FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&bitstream1), FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&bitstream2), FPGA_OK); EXPECT_EQ(xfpga_fpgaDestroyObject(&container), FPGA_OK); } /** * @test fpga_sysfs_02 * sysfs_write_u64 */ TEST_P(sysfs_c_mock_p, fpga_sysfs_02) { fpga_result result; std::string str = sysfs_fme.c_str() + std::string("/socket_id"); // valid path result = sysfs_write_u64(str.c_str(), 0); EXPECT_EQ(result, FPGA_OK); } /** * @test fpga_sysfs_02 * sysfs_write_u64_decimal */ TEST_P(sysfs_c_mock_p, fpga_sysfs_03) { fpga_result result; std::string str = sysfs_fme.c_str() + std::string("/socket_id"); // valid path result = sysfs_write_u64_decimal(str.c_str(), 0x100); EXPECT_EQ(result, FPGA_OK); } /* * @test sysfs * @brief Tests: sysfs_get_port_error_path @details When passed with valid argument return 0 * and port error sysfs path
* When passed with invalid argument return * FPGA_INVALID_PARAM
*/ TEST_P(sysfs_c_mock_p, fpga_sysfs_04) { fpga_result result; char sysfs_path[SYSFS_PATH_MAX] = { 0 }; result = sysfs_get_port_error_path(handle_, sysfs_path); EXPECT_EQ(result, FPGA_OK); result = sysfs_get_port_error_path(handle_, NULL); EXPECT_EQ(result, FPGA_INVALID_PARAM); } /* * @test sysfs * @brief Tests: sysfs_get_port_error_clear_path @details When passed with valid argument return 0 * and port error clear sysfs path
* When passed with invalid argument return * FPGA_INVALID_PARAM
*/ TEST_P(sysfs_c_mock_p, fpga_sysfs_05) { fpga_result result; char sysfs_path[SYSFS_PATH_MAX] = { 0 }; result = sysfs_get_port_error_clear_path(handle_, sysfs_path); EXPECT_EQ(result, FPGA_OK); result = sysfs_get_port_error_clear_path(handle_, NULL); EXPECT_EQ(result, FPGA_INVALID_PARAM); } /* * @test sysfs * @brief Tests: sysfs_get_fme_temp_path @details When passed with valid argument return 0 * and fme temp sysfs path
* When passed with invalid argument return * FPGA_INVALID_PARAM
*/ TEST_P(sysfs_c_mock_p, fpga_sysfs_06) { fpga_result result; char sysfs_path[SYSFS_PATH_MAX] = { 0 }; result = sysfs_get_fme_temp_path(tokens_[0], sysfs_path); EXPECT_EQ(result, FPGA_OK); result = sysfs_get_fme_temp_path(tokens_[0], NULL); EXPECT_EQ(result, FPGA_INVALID_PARAM); } /* * @test sysfs * @brief Tests: sysfs_get_fme_perf_path @details When passed with valid argument return 0 * and fme perf sysfs path
* When passed with invalid argument return * FPGA_INVALID_PARAM
*/ TEST_P(sysfs_c_mock_p, fpga_sysfs_07) { fpga_result result; char sysfs_path[SYSFS_PATH_MAX] = { 0 }; result = sysfs_get_fme_perf_path(tokens_[0], sysfs_path); EXPECT_EQ(result, FPGA_OK); result = sysfs_get_fme_perf_path(tokens_[0], NULL); EXPECT_EQ(result, FPGA_INVALID_PARAM); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_c_mock_p, ::testing::ValuesIn(test_platform::mock_platforms({ "skx-p","dcp-rc", "dcp-vc" }))); class sysfs_dfl_c_mock_p : public sysfs_c_mock_p { }; /* * @test sysfs * @brief Tests: sysfs_get_fme_perf_path @details When passed with valid argument return 0 * and fme perf sysfs path
* When passed with invalid argument returns * FPGA_INVALID_PARAM
* When passed with valid argument on * unsupported plaform returns * FPGA_NOT_FOUND
*/ TEST_P(sysfs_dfl_c_mock_p, fpga_sysfs_08) { fpga_result result; char sysfs_path[SYSFS_PATH_MAX] = { 0 }; result = sysfs_get_fme_perf_path(tokens_[0], sysfs_path); EXPECT_EQ(result, FPGA_NOT_FOUND); result = sysfs_get_fme_perf_path(tokens_[0], NULL); EXPECT_EQ(result, FPGA_INVALID_PARAM); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_dfl_c_mock_p, ::testing::ValuesIn(test_platform::mock_platforms({ "skx-p-dfl0_patchset2" }))); class sysfs_power_mock_p : public sysfs_c_mock_p { }; /* * @test sysfs * @brief Tests: sysfs_get_fme_pwr_path @details When passed with valid argument return 0 * and fme power sysfs path
* When passed with invalid argument returns * FPGA_INVALID_PARAM
* When passed with valid argument on * unsupported plaform returns * FPGA_NOT_FOUND
*/ TEST_P(sysfs_power_mock_p, fpga_sysfs_09) { fpga_result result; char sysfs_path[SYSFS_PATH_MAX] = { 0 }; result = sysfs_get_fme_pwr_path(tokens_[0], sysfs_path); EXPECT_EQ(result, FPGA_NOT_FOUND); result = sysfs_get_fme_pwr_path(tokens_[0], NULL); EXPECT_EQ(result, FPGA_INVALID_PARAM); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_power_mock_p, ::testing::ValuesIn(test_platform::mock_platforms({ "dcp-rc", "dcp-vc" }))); class sysfs_bmc_mock_p : public sysfs_c_mock_p { }; /* * @test sysfs * @brief Tests: sysfs_get_bmc_path @details When passed with valid argument return 0 * and bmc sysfs path
* When passed with invalid argument return * FPGA_INVALID_PARAM
*/ TEST_P(sysfs_bmc_mock_p, fpga_sysfs_10) { fpga_result result; char sysfs_path[SYSFS_PATH_MAX] = { 0 }; result = sysfs_get_bmc_path(tokens_[0], sysfs_path); EXPECT_EQ(result, FPGA_OK); result = sysfs_get_bmc_path(tokens_[0], NULL); EXPECT_EQ(result, FPGA_INVALID_PARAM); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_bmc_mock_p, ::testing::ValuesIn(test_platform::mock_platforms({ "dcp-rc" }))); class sysfs_max10_mock_p : public sysfs_c_mock_p { }; /* * @test sysfs * @brief Tests: sysfs_get_max10_path @details When passed with valid argument return 0 * and max10 sysfs path
* When passed with invalid argument return * FPGA_INVALID_PARAM
*/ TEST_P(sysfs_max10_mock_p, fpga_sysfs_11) { fpga_result result; char sysfs_path[SYSFS_PATH_MAX] = { 0 }; result = sysfs_get_max10_path(tokens_[0], sysfs_path); EXPECT_EQ(result, FPGA_OK); result = sysfs_get_max10_path(tokens_[0], NULL); EXPECT_EQ(result, FPGA_INVALID_PARAM); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_max10_mock_p, ::testing::ValuesIn(test_platform::mock_platforms({ "dcp-vc" }))); class sysfs_c_mock_no_drv_p : public ::testing::TestWithParam { protected: sysfs_c_mock_no_drv_p() {} }; /** * @test sysfs_get_pr_id * @details sysfs_get_pr_id given invalid path parameters. * It returns FPGA_NOT_FOUND. */ TEST_P(sysfs_c_mock_no_drv_p, sysfs_get_pr_id) { int dev = 0; int subdev = 0; fpga_guid guid; auto res = sysfs_get_pr_id(dev, subdev, guid); EXPECT_EQ(res, FPGA_NOT_FOUND); } /** * @test sysfs_get_afu_id * @details sysfs_get_afu_id given invalid path parameters. * It returns FPGA_NOT_FOUND. */ TEST_P(sysfs_c_mock_no_drv_p, sysfs_get_afu_id) { int dev = 0; int subdev = 0; fpga_guid guid; auto res = sysfs_get_afu_id(dev, subdev, guid); EXPECT_EQ(res, FPGA_NOT_FOUND); } /** * @test sysfs_get_socket_id * @details sysfs_get_socket_id given invalid parameters. * It returns FPGA_NOT_FOUND. */ TEST_P(sysfs_c_mock_no_drv_p, sysfs_get_socket_id) { int dev = 0; int subdev = 0; uint8_t socket_id; auto res = sysfs_get_socket_id(dev, subdev, &socket_id); EXPECT_EQ(res, FPGA_NOT_FOUND); } /** * @test sysfs_get_slots * @details sysfs_get_slots given a valid parameters * return FPGA_NOT_FOUND from sysfs_read_u32 */ TEST_P(sysfs_c_mock_no_drv_p, sysfs_get_slots) { int dev = 0; int subdev = 0; uint32_t u32; auto res = sysfs_get_slots(dev, subdev, &u32); EXPECT_NE(res, FPGA_OK); } /** * @test sysfs_get_bitstream_id * @details sysfs_get_bitstream_id given a valid parameters * return FPGA_NOT_FOUND from sysfs_read_u64 */ TEST_P(sysfs_c_mock_no_drv_p, sysfs_get_bitstream_id) { int dev = 0; int subdev = 0; uint64_t u64; auto res = sysfs_get_bitstream_id(dev, subdev, &u64); EXPECT_NE(res, FPGA_OK); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_c_mock_no_drv_p, ::testing::ValuesIn(test_platform::mock_platforms())); class sysfs_sockid_c_mock_p : public sysfs_c_mock_p { }; /** * @test fpga_sysfs_02 * sysfs_write_u64 */ TEST_P(sysfs_sockid_c_mock_p, fpga_sysfs_02) { fpga_result result; std::string str = sysfs_fme.c_str() + std::string("/socket_id"); // valid path result = sysfs_write_u64(str.c_str(), 0); EXPECT_EQ(result, FPGA_OK); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_sockid_c_mock_p, ::testing::ValuesIn(test_platform::mock_platforms({ "skx-p","dcp-rc","dcp-vc" }))); class sysfs_sockid_c_p : public sysfs_c_p { }; /** * @test fpga_sysfs_02 * @brief Tests: sysfs_read_int,sysfs_read_u32 * sysfs_read_u32_pair,sysfs_read_u64 * sysfs_read_u64,sysfs_write_u64 *..........get_port_sysfs,sysfs_read_guid */ TEST_P(sysfs_sockid_c_p, fpga_sysfs_02) { fpga_result result; int i; uint32_t u32; uint32_t u1; uint32_t u2; uint64_t u64; // Empty input path string result = sysfs_read_int("", NULL); EXPECT_NE(result, FPGA_OK); // NULL input parameters result = sysfs_read_int(NULL, NULL); EXPECT_NE(result, FPGA_OK); // Invalid input path result = sysfs_read_int("/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme.10", NULL); EXPECT_NE(result, FPGA_OK); result = sysfs_read_int(sysfs_fme.c_str(), NULL); EXPECT_NE(result, FPGA_OK); // Valid input path std::string str = sysfs_fme.c_str() + std::string("/socket_id"); result = sysfs_read_int(str.c_str(), &i); EXPECT_EQ(result, FPGA_OK); // Empty input path string result = sysfs_read_int("", NULL); EXPECT_NE(result, FPGA_OK); // Invalid input parameters result = sysfs_read_u32(NULL, NULL); EXPECT_NE(result, FPGA_OK); // Invalid input path result = sysfs_read_u32("/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme.10", NULL); EXPECT_NE(result, FPGA_OK); result = sysfs_read_u32(sysfs_fme.c_str(), NULL); EXPECT_NE(result, FPGA_OK); // Valid input path result = sysfs_read_u32(str.c_str(), &u32); EXPECT_EQ(result, FPGA_OK); // Invalid input parameters result = sysfs_read_u32_pair(NULL, NULL, NULL, '\0'); EXPECT_NE(result, FPGA_OK); // Invalid input parameters result = sysfs_read_u32_pair(NULL, NULL, NULL, 'a'); EXPECT_NE(result, FPGA_OK); // Invalid input 'sep' character result = sysfs_read_u32_pair(str.c_str(), &u1, &u2, '\0'); EXPECT_NE(result, FPGA_OK); // Invalid input path value result = sysfs_read_u32_pair(str.c_str(), &u1, &u2, 'a'); EXPECT_NE(result, FPGA_OK); // Invalid input path type result = sysfs_read_u32_pair(sysfs_fme.c_str(), &u1, &u2, 'a'); EXPECT_NE(result, FPGA_OK); // Invalid input path result = sysfs_read_u32_pair( "/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme.10", &u1, &u2, 'a'); EXPECT_NE(result, FPGA_OK); // Empty input path string result = sysfs_read_u64("", NULL); EXPECT_NE(result, FPGA_OK); // NULL input parameters result = sysfs_read_u64(NULL, NULL); EXPECT_NE(result, FPGA_OK); // Invalid input path result = sysfs_read_u64("/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme.10", NULL); EXPECT_NE(result, FPGA_OK); // Valid input path result = sysfs_read_u64(str.c_str(), &u64); EXPECT_EQ(result, FPGA_OK); // Invalid input parameters result = sysfs_write_u64(NULL, 0); EXPECT_NE(result, FPGA_OK); result = sysfs_write_u64(sysfs_fme.c_str(), 0x100); EXPECT_NE(result, FPGA_OK); result = sysfs_write_u64_decimal(NULL, 0); EXPECT_NE(result, FPGA_OK); result = sysfs_write_u64_decimal(sysfs_fme.c_str(), 0x100); EXPECT_NE(result, FPGA_OK); // Invalid input parameters fpga_guid guid; result = sysfs_read_guid(NULL, NULL); EXPECT_NE(result, FPGA_OK); result = sysfs_read_guid( "/sys/class/fpga/intel-fpga-dev.0/intel-fpga-fme.10/", guid); EXPECT_NE(result, FPGA_OK); // NULL input parameters result = get_port_sysfs(NULL, NULL); EXPECT_NE(result, FPGA_OK); // NULL handle result = get_port_sysfs(NULL, (char *)str.c_str()); EXPECT_NE(result, FPGA_OK); // NULL handle result = get_fpga_hw_type(NULL, NULL); EXPECT_NE(result, FPGA_OK); } /** * @test make_region * @details Given valid parameters to make_regions but failed on malloc, * it returns nullptr for sysfs_fpga_region. */ TEST_P(sysfs_sockid_c_p, make_regions) { sysfs_fpga_region *fpga_region; sysfs_fpga_device device; std::string name = "fme"; int num = 1; fpga_objtype type = FPGA_DEVICE; test_system::instance()->invalidate_malloc(); fpga_region = make_region(&device, const_cast(name.c_str()), num, type); EXPECT_EQ(fpga_region, nullptr); } /** * @test sysfs_get_guid * @details Given invalid parameters to sysfs_get_guid. * it returns FPGA_EXCEPTION. When an invalid path is * passed in, it returns FPGA_NOT_FOUND. */ TEST_P(sysfs_sockid_c_p, sysfs_get_guid_neg) { fpga_guid guid; _fpga_token *tok = static_cast<_fpga_token *>(tokens_[0]); std::string sysfspath = tok->sysfspath; EXPECT_EQ(sysfs_get_guid(nullptr, nullptr, guid),FPGA_EXCEPTION); EXPECT_EQ(sysfs_get_guid(tokens_[0], nullptr, guid),FPGA_EXCEPTION); EXPECT_EQ(sysfs_get_guid(nullptr, const_cast(sysfspath.c_str()), guid),FPGA_EXCEPTION); sysfspath = ""; EXPECT_EQ(sysfs_get_guid(tokens_[0], const_cast(sysfspath.c_str()), guid),FPGA_NOT_FOUND); } /** * @test sysfs_path_is_valid * @details Given invalid parameters to sysfs_path_is_valid. * it returns FPGA_NOT_FOUND. */ TEST_P(sysfs_sockid_c_p, sysfs_path_is_valid) { EXPECT_EQ(sysfs_path_is_valid(nullptr, nullptr), FPGA_INVALID_PARAM); } /** * @test get_port_sysfspath * @details When token's sysfs is invalid for get_port_sysfspath. * it returns FPGA_INVALID_PARAM. */ TEST_P(sysfs_sockid_c_p, get_port_sysfs) { _fpga_handle *h = static_cast<_fpga_handle *>(handle_); _fpga_token *tok = static_cast<_fpga_token *>(h->token); EXPECT_EQ(get_port_sysfs(handle_, tok->sysfspath), FPGA_OK); } INSTANTIATE_TEST_CASE_P(sysfs_c, sysfs_sockid_c_p, ::testing::ValuesIn(test_platform::platforms({"skx-p","dcp-rc","dcp-vc"}))); /** * @test match_region * @details Given an input string that matches the format used
* by the kernel driver when making region (platform) devices. * When I call re_match_region with the input string * Then the return code is FPGA_OK * And the output parameters match the "fme" string portion
* and the number portion. */ TEST(sysfs_regex, match_region) { const char *fmt = "intel-fpga-(fme|port)\\.([0-9]+)"; char buffer[8]; int num = -1; char inpstr[] = "intel-fpga-fme.9"; EXPECT_EQ(re_match_region(fmt, inpstr, buffer, sizeof(buffer), &num), FPGA_OK); EXPECT_STREQ(buffer, "fme"); EXPECT_EQ(num, 9); } /** * @test match_region_neg * @details Given an input string that does not match the format used
* by the kernel driver when making region (platform) devices. * When I call re_match_region with the input string or an invalid
* parameter * Then the return code is either FPGA_NOT_FOUND or FPGA_INVALID_PARAM */ TEST(sysfs_regex, match_region_neg) { const char *fmt = "intel-fpga-(fme|port)\\.([0-9]+)"; char buffer[8]; int num = -1; char badstr[] = "intel-fpga-abc.0"; EXPECT_EQ(re_match_region(fmt, badstr, buffer, sizeof(buffer), &num), FPGA_NOT_FOUND); EXPECT_EQ(re_match_region(nullptr, badstr, buffer, sizeof(buffer), &num), FPGA_INVALID_PARAM); EXPECT_EQ(re_match_region(fmt, nullptr, buffer, sizeof(buffer), &num), FPGA_INVALID_PARAM); EXPECT_EQ(re_match_region(fmt, badstr, nullptr, 0, &num), FPGA_INVALID_PARAM); EXPECT_EQ(re_match_region(fmt, badstr, buffer, sizeof(buffer), nullptr), FPGA_INVALID_PARAM); }