// 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 <opae/utils.h>
#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 <fstream>
#include <opae/enum.h>
#include <opae/fpga.h>
#include <opae/properties.h>
#include <sys/types.h>
#include <uuid/uuid.h>
#include <string>
#include <vector>
#include "xfpga.h"
#include <fcntl.h>
#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<std::string> {
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<uint64_t, test_device> 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<std::map<uint64_t, test_device>*>(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<uint64_t, test_device> 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<std::string> {
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<fpga_token, 2> 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<char *>(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<char> 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<char> 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<char> 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<char *>(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<char *>(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 <br>
* When passed with invalid argument return
* FPGA_INVALID_PARAM <br>
*/
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 <br>
* When passed with invalid argument return
* FPGA_INVALID_PARAM <br>
*/
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 <br>
* When passed with invalid argument return
* FPGA_INVALID_PARAM <br>
*/
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 <br>
* When passed with invalid argument return
* FPGA_INVALID_PARAM <br>
*/
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 <br>
* When passed with invalid argument returns
* FPGA_INVALID_PARAM <br>
* When passed with valid argument on
* unsupported plaform returns
* FPGA_NOT_FOUND <br>
*/
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 <br>
* When passed with invalid argument returns
* FPGA_INVALID_PARAM <br>
* When passed with valid argument on
* unsupported plaform returns
* FPGA_NOT_FOUND <br>
*/
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 <br>
* When passed with invalid argument return
* FPGA_INVALID_PARAM <br>
*/
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 <br>
* When passed with invalid argument return
* FPGA_INVALID_PARAM <br>
*/
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<std::string> {
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<char*>(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<char*>(sysfspath.c_str()), guid),FPGA_EXCEPTION);
sysfspath = "";
EXPECT_EQ(sysfs_get_guid(tokens_[0], const_cast<char*>(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<br>
* 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<br>
* 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<br>
* by the kernel driver when making region (platform) devices.
* When I call re_match_region with the input string or an invalid<br>
* 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);
}