LCOV - code coverage report
Current view: top level - oks/apps - oks_check_schema.cxx (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 95 0
Test Date: 2026-10-11 15:05:20 Functions: 0.0 % 11 0

            Line data    Source code
       1              : /**
       2              :  * @file oks_check_schema.cxx
       3              :  *
       4              :  * Program to do some simple integrity checks on a schema file
       5              :  *
       6              :  * This is part of the DUNE DAQ Application Framework, copyright 2025.
       7              :  * Licensing/copyright details are in the COPYING file that you should have
       8              :  * received with this code.
       9              :  */
      10              : 
      11              : #include "CLI/CLI.hpp"
      12              : #include "logging/Logging.hpp"
      13              : #include "oks/class.hpp"
      14              : #include "oks/file.hpp"
      15              : #include "oks/kernel.hpp"
      16              : #include "oks/relationship.hpp"
      17              : 
      18              : #include <fmt/core.h>
      19              : #include <set>
      20              : #include <string>
      21              : 
      22              : using namespace dunedaq::oks;
      23              : 
      24              : int
      25            0 : main(int argc, char** argv)
      26              : {
      27            0 :   enum Exitcode : int
      28              :   {
      29              :     SUCCESS,
      30              :     BADCMD,
      31              :     NOFILE,
      32              :     LOADFAIL,
      33              :     BADRELATIONSHIP,
      34              :     UNRESOLVED,
      35              :     MISSING_INCLUDE
      36              :   };
      37              : 
      38            0 :   CLI::App app{
      39            0 :     "Check consistency of OKS schema file\n" +
      40            0 :     fmt::format("Return codes: {} Success, file is OK\n", Exitcode::SUCCESS) +
      41            0 :     fmt::format("              {} Bad command line\n", Exitcode::BADCMD) +
      42            0 :     fmt::format("              {} Failed to find file\n", Exitcode::NOFILE) +
      43            0 :     fmt::format("              {} Failed to load file -- invalid schema\n", Exitcode::LOADFAIL) +
      44            0 :     fmt::format("              {} File contains relationship to non loaded class\n", Exitcode::BADRELATIONSHIP) +
      45            0 :     fmt::format("              {} File contains object with relationship to non loaded class/object\n",
      46            0 :                 Exitcode::UNRESOLVED) +
      47            0 :     fmt::format("              {} File refers to class in file not directly included\n", Exitcode::MISSING_INCLUDE)
      48            0 :   };
      49              : 
      50            0 :   std::string filename;
      51            0 :   app.add_option("-f,--file", filename, "Schema file")->required();
      52              : 
      53            0 :   CLI11_PARSE(app, argc, argv);
      54              : 
      55            0 :   OksKernel kernel;
      56            0 :   OksFile* file{ nullptr };
      57            0 :   try {
      58            0 :     file = kernel.load_file(filename);
      59            0 :   } catch (FailedLoadFile& fail) {
      60            0 :     TLOG() << fail.what() << "\n";
      61            0 :     return Exitcode::LOADFAIL;
      62            0 :   } catch (CanNotOpenFile& fail) {
      63            0 :     TLOG() << fail.what() << "\n";
      64            0 :     return Exitcode::NOFILE;
      65            0 :   } catch (std::exception& exc) {
      66            0 :     TLOG() << exc.what() << "\n";
      67            0 :     return Exitcode::LOADFAIL;
      68            0 :   }
      69              : 
      70            0 :   if (file == nullptr) {
      71            0 :     TLOG() << "Failed to load " << filename;
      72            0 :     return Exitcode::LOADFAIL;
      73              :   }
      74              : 
      75            0 :   std::set<std::string> includes;
      76            0 :   for (const auto& ifile : file->get_include_files()) {
      77            0 :     includes.insert(ifile);
      78            0 :     TLOG_DEBUG(2) << "direct inserting " << ifile << "\n";
      79              :   }
      80              : 
      81            0 :   Exitcode result = Exitcode::SUCCESS;
      82              : 
      83            0 :   if (filename.ends_with(".schema.xml")) {
      84              :     // Check classes defined in this schema file
      85            0 :     for (auto [name, oks_class] : kernel.classes()) {
      86              : 
      87            0 :       if (file->get_full_file_name() != oks_class->get_file()->get_full_file_name()) {
      88              :         // Class not defined in this file
      89            0 :         continue;
      90              :       }
      91              : 
      92            0 :       auto cfile = oks_class->get_file()->get_short_file_name();
      93            0 :       if (cfile != file->get_short_file_name() && !includes.contains(cfile)) {
      94            0 :         TLOG() << "Error " << filename << " misses include of " << cfile << " required by " << name;
      95            0 :         if (result == Exitcode::SUCCESS) {
      96            0 :           result = Exitcode::MISSING_INCLUDE;
      97              :         }
      98              :       }
      99            0 :       auto relationships = oks_class->direct_relationships();
     100            0 :       if (relationships != nullptr) {
     101            0 :         for (auto rel : *relationships) {
     102            0 :           auto rel_class = rel->get_class_type();
     103            0 :           if (rel_class == nullptr) {
     104            0 :             TLOG() << "Error class '" << name << "' has relationship '" << rel->get_name() << "' to a class '"
     105            0 :                    << rel->get_type() << "' that is not loaded\n";
     106            0 :             if (result == Exitcode::SUCCESS) {
     107            0 :               result = Exitcode::BADRELATIONSHIP;
     108              :             }
     109              :           }
     110              :         }
     111              :       }
     112              :       // Could check super-classes but we wouldn't have got past the
     113              :       // load if they referred to a missing class.
     114            0 :     }
     115            0 :   } else if (filename.ends_with(".data.xml")) {
     116              : 
     117            0 :     auto check_relationship = [&](OksData* data) {
     118            0 :       auto relobj = data->data.OBJECT;
     119            0 :       if (relobj == nullptr) {
     120            0 :         return;
     121              :       }
     122            0 :       auto ofile_name = relobj->get_file()->get_short_file_name();
     123            0 :       if (file->get_short_file_name() != ofile_name && !includes.contains(ofile_name)) {
     124            0 :         TLOG() << "Error " << filename << " misses include of " << ofile_name << " required by " << relobj->GetId();
     125            0 :         if (result == Exitcode::SUCCESS) {
     126            0 :           result = Exitcode::MISSING_INCLUDE;
     127              :         }
     128              :       }
     129            0 :     };
     130              : 
     131            0 :     auto data_objs = kernel.create_list_of_data_objects(file);
     132            0 :     for (auto obj : *data_objs) {
     133              :       // Check that any objects defined in this data file have their
     134              :       // corresponding schema file included directly in this file
     135            0 :       auto oks_class = obj->GetClass();
     136            0 :       auto cfile = oks_class->get_file()->get_short_file_name();
     137            0 :       if (!includes.contains(cfile)) {
     138            0 :         TLOG() << "Error " << filename << " misses include of " << cfile << " required by " << obj->GetId()
     139            0 :                << " of class " << oks_class->get_name();
     140            0 :         if (result == Exitcode::SUCCESS) {
     141            0 :           result = Exitcode::MISSING_INCLUDE;
     142              :         }
     143              :       }
     144              : 
     145              :       // Check that any objects referenced by objects in this file are
     146              :       // either defined in this file or one that is directly included
     147            0 :       auto relationships = oks_class->all_relationships();
     148            0 :       if (relationships != nullptr) {
     149            0 :         for (auto rel : *relationships) {
     150            0 :           auto relval = obj->GetRelationshipValue(rel->get_name());
     151            0 :           if (relval == nullptr) {
     152            0 :             continue;
     153              :           }
     154            0 :           if (rel->get_high_cardinality_constraint() == OksRelationship::Many) {
     155            0 :             for (OksData::List::iterator iter = relval->data.LIST->begin(); iter != relval->data.LIST->end(); ++iter) {
     156            0 :               check_relationship(*iter);
     157              :             }
     158              :           } else {
     159            0 :             check_relationship(relval);
     160              :           }
     161              :         }
     162              :       }
     163            0 :     }
     164            0 :     delete data_objs;
     165              :   }
     166              : 
     167            0 :   auto status = kernel.get_bind_objects_status();
     168            0 :   if (!status.empty()) {
     169            0 :     if (result == Exitcode::SUCCESS) {
     170            0 :       result = Exitcode::UNRESOLVED;
     171              :     }
     172              :   }
     173              : 
     174            0 :   return result;
     175            0 : }
        

Generated by: LCOV version 2.0-1