/* ************************************************************************ * Copyright (C) 2018-2024 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 cop- * ies 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 IM- * PLIED, 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 CONNE- * CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * ************************************************************************ */ #pragma once #include "rocblas_arguments.hpp" #include "test_cleanup.hpp" #include #include #include #include #include #include #include #include #include #include #if __has_include() #include namespace fs = std::filesystem; #elif __has_include() #include namespace fs = std::experimental::filesystem; #else #error no filesystem found #endif // Class used to read Arguments data into the tests class RocBLAS_TestData { // data filename static auto& filename() { static std::string filename = "(Uninitialized data. RocBLAS_TestData::set_filename needs to be called first.)"; return filename; } // filter iterator class iterator : public std::istream_iterator { bool (*const filter)(const Arguments&) = nullptr; // Skip entries for which validate or filter returns false void skip_filter() { static auto endIter = std::istream_iterator{}; // warning we may update the Arguments in validate, thus the const_cast while(*this != endIter && !(const_cast(**this).validate())) ++*static_cast*>(this); if(filter) while(*this != endIter && !filter(**this)) ++*static_cast*>(this); } public: // Constructor takes a filter and iterator iterator(bool filter(const Arguments&), std::istream_iterator iter) : std::istream_iterator(iter) , filter(filter) { skip_filter(); } // Default end iterator and nullptr filter iterator() = default; // Preincrement iterator operator with filtering iterator& operator++() { ++*static_cast*>(this); skip_filter(); return *this; } // We do not need a postincrement iterator operator // We delete it here so that the base class's isn't silently called // To implement it, use "auto old = *this; ++*this; return old;" iterator operator++(int) = delete; }; public: // Initialize filename, optionally removing it at exit static void set_filename(std::string name, bool remove_atexit = false) { filename() = std::move(name); if(remove_atexit) { auto cleanup = [] { // Best-effort removal of generated temp file. Use non-throwing // overload because failure during process exit must not abort. std::error_code ec; fs::remove(filename(), ec); }; atexit(cleanup); at_quick_exit(cleanup); } } // begin() iterator which accepts an optional filter. static iterator begin(bool filter(const Arguments&) = nullptr) { static std::ifstream* ifs = nullptr; // If this is the first time, or after test_cleanup::cleanup() has been called if(!ifs) { std::string fileToOpen = filename(); // Allocate a std::ifstream and register it to be deleted during cleanup ifs = test_cleanup::allocate( &ifs, fileToOpen, std::ifstream::in | std::ifstream::binary); if(!ifs || ifs->fail()) { rocblas_cerr << "Cannot open: " << fileToOpen << std::endl; exit(EXIT_FAILURE); } } // We re-seek the file back to position 0 ifs->clear(); ifs->seekg(0); // Validate the data file format Arguments::validate(*ifs); // We create a filter iterator which will choose only the test cases we want right now. // This is to preserve Gtest structure while not creating no-op tests which "always pass". return iterator(filter, std::istream_iterator(*ifs)); } // end() iterator static iterator end() { return {}; } };