Coding Style and Quality Assurance

Code Quality

Coding Styles

This project adheres to the following coding styles:

Deviations from the GCSG

  • Exceptions are allowed.
  • Name mutator functions without set_ prefix.
  • Multiple implementation inheritance is allowed (mostly for mixins).
  • One-liner if statements are not allowed. It interferes with interactive debugging and code coverage analysis tools (may hide uncovered lines).

Deviations from the Qt Style

  • 80-character line length limit (vs. 100).

  • Exceptions are allowed.

  • using-directives are forbidden.

  • Prefer anonymous namespace to static keyword.

  • The GCSG style include of headers:

    • Sections:

      1. Related header (including ui header)
      2. C-system headers
      3. C++ system headers
      4. Qt headers
      5. Other libraries’ headers
      6. Project Core headers
      7. Project GUI headers
    • Sections are grouped by a blank line.

    • Within each section the includes are ordered alphabetically.

  • Class Format:

    • Section order:

      1. public: member functions
      2. signals: (public by default in Qt5)
      3. public slots:
      4. protected: member functions
      5. protected slots:
      6. private: member functions
      7. private slots:
      8. private: all data members
    • No blank lines after access specifiers.

    • One blank line before access specifiers except for the first one.

    • Declaration order (the GCSG style):

      1. Using declarations, Typedefs, Structs/Classes, and Enums.
      2. Static const data members
      3. Constructors
      4. Destructors
      5. Methods
      6. Data members
  • Automatic connection of signals and slots is forbidden.

  • Using literal 0 for pointers is forbidden. Only nullptr is allowed for null pointers.

Additional Coding Conventions

  • Use modern C++ (C++14). Refer to C++ Core Guidelines for best practices.

  • Do not use inline when defining a function in a class definition. It is implicitly inline. Do not use inline as an optimization hint for the compiler. Only reasonable use of inline is for the linker to avoid violating the ODR.

  • !bool vs. bool == false

    1. Prefer using the negation and implicit conversions to bool only with Boolean or Boolean-like values (bool, nullptr, 0 for non-existence, etc.). An example abuse of the negation and implicit conversion to bool would be:
    double checked_div(double x, double y) {
        if (!y)                      // Bad. Looks like 'if (no y) then fail'.
            throw domain_error("");  // More explicit (y == 0) is better.
        return x / y;
    }
    
    1. However, if the expression is long (3 or more parts), prefer comparing with the value (false, 0, etc.) explicitly for readability:
    if (var.getter().data().empty() == false);
    
    1. Avoid inverted or negated logic if possible.
Core C++ Code
  • Exceptions are forbidden in analysis code.

  • RTTI (typeid, dynamic_cast, dynamic_pointer_cast, etc.) is forbidden in analysis code.

  • Defensive Programming. Check all preconditions, postconditions, invariants, and assumptions with the assert macro wherever possible in analysis code. Consider supplying an error message to clarify the assertion, for example, assert(!node->mark() && "Detected a cycle!").

  • If function input parameters or return values are pointers (raw or smart), they are never null pointers unless explicitly specified. Null-based logic must be rare, localized, and explicit (consider using boost::optional instead).

  • Consider supplying a typedef or alias declaration for common smart pointers.

    • ClassNamePtr for shared, unique, and intrusive pointers
    • ClassNameWeakPtr for weak pointers
  • Function call qualification conventions:

    • Unqualified calls customizable by or relying on the ADL must make it explicit in the documentation and comments.

    • In definitions of member functions:

      • Explicitly qualify calls to inherited non-virtual member functions with the corresponding base class names, e.g., BaseClassName::Foo().
      • Qualify virtual functions to be overridden by design as this->Foo().
      • Qualify a call to a free function with its namespace, e.g., scram::Foo().
    • In definitions of free functions, calls to other free functions in the enclosing namespace can be unqualified.

  • Declare a getter function before a setter function for a corresponding member variable.

  • Declare getter and setter functions before other complex member functions.

  • Domain-specific Probability naming rules:

    • If a probability variable is a member variable of a class, abbreviate it to p_. Its getter/setter functions should have corresponding names, i.e., p() and p(double value). Append extra description after p_, e.g., p_total_ (a la Semantic Hungarian). Avoid abbreviating the name to prob or fully spelling it to probability.

    • For non-member probability variables:

      • Prefer prefixing with p_ (a la Semantic Hungarian) if the name has more description to the probability value, e.g., p_not_event.
      • Prefer prob abbreviation for single word names indicating general probability values.
    • Prefer spelling Probability fully for cases not covered above (class/function/namespace/typedef/…), e.g., CalculateProbability. Avoid abbreviating the name, e.g., CalculateProb.

  • Prefer the terminology and concepts of Boolean algebra and graph theory to the terminology and concepts of risk analysis in analysis code. For example, a Boolean product is more general and appropriate for analysis facilities than cut sets or prime implicants.

    • There is no Boolean operator for the K-out-of-N logic. This gate in fault tree analysis has many names (Voting, Combination, atleast, K/N, etc.), and there doesn’t seem to be a consensus among sources and tools. The Open-PSA MEF “atleast” best captures the nature of the gate; however, the “atleast” is awkward to use in code and API (Atleast vs. AtLeast vs. atleast vs. at_least). In SCRAM, the “vote” word must be used consistently to represent this gate in code and API. The code that deals with the Open-PSA MEF may use the “atleast”.
  • In performance-critical analysis code (BDD variable ordering, Boolean formula rewriting/preprocessing, etc.), avoid platform/implementation-dependent constructs (iterating over unordered containers, unstable sorts, using an object address as its identity, etc.). The performance profile must be stable across platforms.

GUI Code
  • Avoid Qt containers whenever possible. Prefer STL/Boost containers and constructs.
  • Upon using Qt containers and constructs, stick to their STL API and usage style as much as possible. Avoid the Java-style API.
  • Upon using Qt specialized containers (e.g., QStringList), do not use a single-element constructor (e.g., QStringList(QString)). Use the initializer list instead.
  • Avoid default arguments in signals and slots.
  • Prefer Qt5 style connections without SIGNAL/SLOT macros.
  • Prefer normalized signatures in connect statements with SIGNAL/SLOT macros.
  • Prefer Qt Designer UI forms over hand-coded GUI.
  • Common Qt includes may be omitted, for example, QString, QList, QStringList, and QDir.
  • Avoid using forward declaration of Qt library classes. Just include the needed headers.
  • Avoid qobject_cast and its flavors. Avoid the RTTI in general.

Monitoring Code Quality

C++

  1. Performance profiling with Gprof, Valgrind, and perf
  2. Code coverage check with Gcov and reporting with Codecov
  3. Memory management bugs and leaks with Valgrind
  4. Static code analysis with Coverity and CppCheck
  5. Cyclomatic complexity analysis with Lizard
  6. Google style conformance check with Cpplint
  7. Common C++ code problem check with cppclean
  8. Consistent code formatting with ClangFormat
  9. Component dependency analysis with cppdep

Python

  1. Code quality and style check with Pylint
  2. Profiling with PyVmMonitor
  3. Code coverage check with coverage and reporting with Codecov
  4. Continuous code quality control on Landscape with Prospector

Targets

Metric Before Release On Release
C++ Code Coverage 80% 95%
C++ Defect Density 0.5 per 1000 SLOC 0.35 per 1000 SLOC
CCN 15 15
Python Code Coverage 80% 95%
Pylint Score 9.0 9.5
Documentation Full Full

Note

C++ defects that count towards the defect density include analysis errors, Coverity report, memory leaks, and known critical bugs.

Note

Utility scripts written in Python are exempt from the test coverage requirement.

Testing and Continuous Integration

In order to facilitate better software quality and quality assurance, full test coverage is attempted through unit, integration, regression, and benchmarking tests. The following tools are used for this purpose:

These tests are automated, and continuous integration is provided by Travis CI and AppVeyor.

Guided fuzz testing is performed with auto-generated analysis input files to discover bugs, bottlenecks, and assumption failures.

Documentation

http://www.osnews.com/images/comics/wtfm.jpg

Good documentation of the code and functionality is the requirement for maintainability and evolution of the project.

The project adheres to the Documentation Driven Development model (DDD talk by Corey Oordt), following the best practices of Agile Documentation, Google Documentation Guide Philosophy and Best Practices.

The documentation for the project is maintained in the reStructuredText format, and the final representations are dynamically generated with Sphinx in various formats (html, pdf, LaTeX).

The code documentation is dynamically generated with Doxygen, which also verifies full documentation coverage.

The source text of the documentation in the code and the reST format must be formatted consistently and with Semantic Linefeeds for maintainability and version control.

Conventions in Documentation “Source Text”

General

  • Prefer the Aralia Input Format for the Boolean formula documentation. This format uses the C-style bit-wise logical operators for formulas.

reST Documentation Style

  • Semantic Linefeeds
  • Two blank lines between sections with bodies
  • One blank line after a header before its body
  • Part # overlined and underlined
  • Chapter * overlined and underlined
  • Section underlining and order =, -, ~, ^, +
  • Point nesting and order -, *, +
  • 4-space indentation
  • 100 character line limit (except for links and paths)
  • No trailing whitespace characters
  • No tabs (spaces only)
  • No excessive blank lines at the end of files

Core Code Documentation Style

  • Semantic Linefeeds

  • Doxygen comments with /// and ///<

  • Comment ordering:

    1. description
    2. tparam
    3. param
    4. returns
    5. pre
    6. post
    7. throws
    8. note
    9. warning
    10. todo
  • Leave one Doxygen blank line between sections

  • Always specify input and output parameters with @param[in,out] arg  Description...

    • Two spaces between parameter and its description
    • The same formatting for template parameters @tparam T  Type desc...
  • The two-space formatting for @throws Error  Description

  • In-code TODOs with Doxygen /// @todo so that Doxygen picks them up.

GUI Code Documentation Style

  • Semantic Linefeeds
  • Leverage Qt Creator for auto-documentation with Doxygen (Javadoc style and ///< for one-liners)
  • The same organization of Doxygen sections as in the core code.

XML Formatting Style

  • 2-space indentation
  • No tabs (spaces only)
  • No trailing whitespace characters
  • No excessive blank lines
  • No spaces around tag opening and closing brackets: <, />, <\, >.
  • Only one space between attributes
  • No spaces around = in attribute value assignment
  • Prefer 100 character line limit
  • Avoid putting several elements on the same line
  • UTF-8 encoding