/*! \file */ /* ************************************************************************ * Copyright (C) 2022-2025 Advanced Micro Devices, Inc. All rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * ************************************************************************ */ #pragma once #include "rocsparse_memory_check.hpp" #include "rocsparse_test_call.hpp" #include "rocsparse_test_check.hpp" #include "rocsparse_test_dispatch.hpp" #include "rocsparse_test_functors.hpp" #include "rocsparse_test_traits.hpp" namespace { template struct rocsparse_test_template { private: using call_t = rocsparse_test_call; using traits_t = rocsparse_test_traits; using functors_t = rocsparse_test_functors; using dispatch_t = rocsparse_test_dispatch; public: template struct test_call_proxy { explicit operator bool() { return true; } void operator()(const Arguments& arg) { const char* name_ROUTINE = rocsparse_test_enum::to_string(ROUTINE); if(!strcmp(arg.function, name_ROUTINE)) { call_t::template testing(arg); } else { std::string s(name_ROUTINE); s += "_bad_arg"; if(!strcmp(arg.function, s.c_str())) { call_t::template testing_bad_arg(arg); } else { std::string s1(name_ROUTINE); s1 += "_extra"; if(!strcmp(arg.function, s1.c_str())) { call_t::testing_extra(arg); } else { FAIL() << "Internal error: Test called with unknown function: " << arg.function; } } } } }; template class PROXY_CALL> struct test_proxy : RocSPARSE_Test { using definition = RocSPARSE_Test; static bool type_filter(const Arguments& arg) { return dispatch_t::template dispatch(arg); } static bool function_filter(const Arguments& arg) { const char* name = rocsparse_test_enum::to_string(ROUTINE); std::string s(name); s += "_bad_arg"; std::string s1(name); s1 += "_extra"; return !strcmp(arg.function, name) || !strcmp(arg.function, s.c_str()) || !strcmp(arg.function, s1.c_str()); } static bool arch_filter(const Arguments& arg) { static int dev; static hipDeviceProp_t prop; static bool query_device = true; if(query_device) { if(hipGetDevice(&dev) != hipSuccess) { return false; } if(hipGetDeviceProperties(&prop, dev) != hipSuccess) { return false; } query_device = false; } if(strncmp("gfx", arg.hardware, 3) == 0) { if(strncmp(arg.hardware, prop.gcnArchName, strlen(arg.hardware)) == 0) { return true; } else { return false; } } if(strncmp("none", arg.skip_hardware, 4) != 0) { const char* b = arg.skip_hardware; const char* e; for(e = b; *e != '\0' && *e != ',' && *e != ' '; ++e) ; while(strncmp("gfx", b, 3) == 0) { if(strncmp(b, prop.gcnArchName, e - b) == 0) { return false; } else { for(; *b != '\0' && *b != ',' && *b != ' '; ++b) ; if(*b == ',') ++b; for(; *b != '\0' && *b == ' '; ++b) ; for(e = b; *e != '\0' && *e != ',' && *e != ' '; ++e) ; } } } return true; } static bool memory_filter(const Arguments& arg) { size_t available_host = rocsparse_memory_check::get_available_host_memory_gb(); size_t available_device = rocsparse_memory_check::get_available_device_memory_gb(); bool can_run = (arg.host_memory_gb <= available_host) && (arg.device_memory_gb <= available_device); if(!can_run) { std::cout << "SKIPPING TEST: Insufficient memory" << std::endl; std::cout << " Required host: " << arg.host_memory_gb << " GB (available: " << available_host << " GB)" << std::endl; std::cout << " Required device: " << arg.device_memory_gb << " GB (available: " << available_device << " GB)" << std::endl; } return can_run; } static std::string name_suffix(const Arguments& arg) { // // Check if this is extra tests. // { const char* name = rocsparse_test_enum::to_string(ROUTINE); std::string s1(name); s1 += "_extra"; if(!strcmp(arg.function, s1.c_str())) { // // Return the name of the test. // return RocSPARSE_TestName{} << arg.name; } } const bool from_file = rocsparse_arguments_has_datafile(arg); std::ostringstream s; switch(traits_t::s_dispatch) { case rocsparse_test_dispatch_enum::t: { s << rocsparse_datatype2string(arg.compute_type); break; } case rocsparse_test_dispatch_enum::it: case rocsparse_test_dispatch_enum::it_sparse_to_dense: case rocsparse_test_dispatch_enum::it_dense_to_sparse: case rocsparse_test_dispatch_enum::it_gather_scatter: { s << rocsparse_indextype2string(arg.index_type_I) << '_' << rocsparse_datatype2string(arg.compute_type); break; } case rocsparse_test_dispatch_enum::ijt: case rocsparse_test_dispatch_enum::ijt_sparse_to_dense: case rocsparse_test_dispatch_enum::ijt_dense_to_sparse: { s << rocsparse_indextype2string(arg.index_type_I) << '_' << rocsparse_indextype2string(arg.index_type_J) << '_' << rocsparse_datatype2string(arg.compute_type); break; } case rocsparse_test_dispatch_enum::ixyt: case rocsparse_test_dispatch_enum::ixyt_axpby: { s << rocsparse_indextype2string(arg.index_type_I) << '_' << rocsparse_datatype2string(arg.x_type) << '_' << rocsparse_datatype2string(arg.y_type) << '_' << rocsparse_datatype2string(arg.compute_type); break; } case rocsparse_test_dispatch_enum::iaxyt: { s << rocsparse_indextype2string(arg.index_type_I) << '_' << rocsparse_datatype2string(arg.a_type) << '_' << rocsparse_datatype2string(arg.x_type) << '_' << rocsparse_datatype2string(arg.y_type) << '_' << rocsparse_datatype2string(arg.compute_type); break; } case rocsparse_test_dispatch_enum::ijaxyt: { s << rocsparse_indextype2string(arg.index_type_I) << '_' << rocsparse_indextype2string(arg.index_type_J) << '_' << rocsparse_datatype2string(arg.a_type) << '_' << rocsparse_datatype2string(arg.x_type) << '_' << rocsparse_datatype2string(arg.y_type) << '_' << rocsparse_datatype2string(arg.compute_type); break; } case rocsparse_test_dispatch_enum::iabct: { s << rocsparse_indextype2string(arg.index_type_I) << '_' << rocsparse_datatype2string(arg.a_type) << '_' << rocsparse_datatype2string(arg.b_type) << '_' << rocsparse_datatype2string(arg.c_type) << '_' << rocsparse_datatype2string(arg.compute_type); break; } case rocsparse_test_dispatch_enum::ijabct: case rocsparse_test_dispatch_enum::ijabct_sddmm: { s << rocsparse_indextype2string(arg.index_type_I) << '_' << rocsparse_indextype2string(arg.index_type_J) << '_' << rocsparse_datatype2string(arg.a_type) << '_' << rocsparse_datatype2string(arg.b_type) << '_' << rocsparse_datatype2string(arg.c_type) << '_' << rocsparse_datatype2string(arg.compute_type); break; } } // // Check if this is bad_arg // { const char* name = rocsparse_test_enum::to_string(ROUTINE); std::string s1(name); s1 += "_bad_arg"; if(!strcmp(arg.function, s1.c_str())) { s << "_bad_arg"; } else { const std::string suffix = functors_t::name_suffix(arg); if(suffix.size() > 0) { s << '_' << suffix; } if(from_file) { s << '_' << rocsparse_filename2string(arg.filename); } } } return RocSPARSE_TestName{} << s.str(); } }; }; template struct rocsparse_test_ixyt_template { template struct test_call : rocsparse_test_invalid { }; template struct test_call::value>::type> : rocsparse_test_template::template test_call_proxy { }; struct test : rocsparse_test_template::template test_proxy { }; }; template struct rocsparse_test_iaxyt_template { template struct test_call : rocsparse_test_invalid { }; template struct test_call::value>::type> : rocsparse_test_template::template test_call_proxy { }; struct test : rocsparse_test_template::template test_proxy { }; }; template struct rocsparse_test_ijaxyt_template { template struct test_call : rocsparse_test_invalid { }; template struct test_call::value>::type> : rocsparse_test_template::template test_call_proxy { }; struct test : rocsparse_test_template::template test_proxy { }; }; template struct rocsparse_test_iabct_template { template struct test_call : rocsparse_test_invalid { }; template struct test_call::value>::type> : rocsparse_test_template::template test_call_proxy { }; struct test : rocsparse_test_template::template test_proxy { }; }; template struct rocsparse_test_ijabct_template { template struct test_call : rocsparse_test_invalid { }; template struct test_call::value>::type> : rocsparse_test_template::template test_call_proxy { }; struct test : rocsparse_test_template::template test_proxy { }; }; template struct rocsparse_test_ijt_template { using check_t = rocsparse_test_check; // template struct test_call : rocsparse_test_invalid { }; // template struct test_call()>::type> : rocsparse_test_template::template test_call_proxy { }; struct test : rocsparse_test_template::template test_proxy { }; }; template struct rocsparse_test_it_template { using check_t = rocsparse_test_check; // template struct test_call : rocsparse_test_invalid { }; // template struct test_call()>::type> : rocsparse_test_template::template test_call_proxy { }; struct test : rocsparse_test_template::template test_proxy { }; }; template struct rocsparse_test_t_template { using check_t = rocsparse_test_check; template struct test_call : rocsparse_test_invalid { }; // template struct test_call()>::type> : rocsparse_test_template::template test_call_proxy { }; struct test : rocsparse_test_template::template test_proxy { }; }; }