Skip to content

Latest commit

 

History

History
1043 lines (592 loc) · 29.4 KB

design.md

File metadata and controls

1043 lines (592 loc) · 29.4 KB
<title>OpenFastTrace Design</title>

Introduction

Acknowledgments

This documents structure is derived from the "arc42" architectural template by Dr. Gernot Starke, Dr. Peter Hruschka.

If you build your own modifications based on this document, please keep the attrbiutions.

Terminology

The terminology from the system requirement specification applies.

Conventions

Syntax Definitions

Syntax definitions in this document use the Augmented Backus-Naur Form.

The following definitions are used frequently throughout the document:

  • ANY - any valid character
  • LINEBREAK = the line break character of the platform

Constraints

Solution Ideas and Strategy

Requirement tracing

The algorithm that checks the requirement links has to follow each link between requester and provider at least once.

Given:

rn, n ∊ {1..N}

pm, m ∊ {1..M}

an,l, l ∊ {1..Ln}

cm,k, k ∊ {1..Mm}

Where r are the requesters, p the providers, a artifacts required by providers and c the coverage provided.

The number of forward links results to:

f = ΣNn = 1 ( Ln )

The number of backward links is:

b = ΣMn = 1 ( Kn )

Lookups in the Naive Approach

The naive approach is to iterate over all required artifacts in all requesters and for each iterate over the complete set of providers to see if coverage exists.

If we assume a scenario where the coverage is 100% and a provider is found in average after iterating over half the providers this gives us the following number of Lookups l:

l = f p / 2

Assuming that each requester needs to be covered by an average of A artifacts we get:

f = r A

l = ( r A ) p / 2

Assuming further that each provider has an average of C back links to provide coverage we further get:

p = r C

l = ( r A ) ( r C ) / 2 = r² A C / 2

Note that A and C can be considered to have a small range in a typical project, depending only on the working style and not on the number of requirements the project need to handle.

In effect the lookup using the naive approach results in a complexity of O(n²). Not good.

Lookups With an Index

If instead of relying on a linear search of all providers, we use an index. We saw earlier that iterating over the requesters results in a complexity of O(n). A proper index lookup should give O(log n) in the worst case. That leaves us with a complexity of O(n log(n)). Better.

Choosing the Index

Since the specification item IDs inherently look similar, load tests need to show which kind of index is best balanced and has the optimal access time.

Context

Technical Constraints

Conventions

Building Block View

Importers

For each specification artifact type OFT uses an importer. The importer uses the specification artifact as data source and reads specification items from it.

Import Event Listener

Importers emit events if they find parts of a specification item in the artifact they are importing.

Specification List Builder

The specification list builder is an import event listener that creates a list of specification items from import events.

Command Line Interpreter

The command line interpreter (CLI) takes parameters given to OFT and parses them. It is responsible for making sense of the parameter contents and issuing help and error messages about the command line syntax.

Linker

The linker is responsible for turning the imported specification items collected by the importers into linked specification items.

Tracer

The tracer consumes the list of linked specification items and evaluates the link status for each link.

Reporter

The reporter consumes the link status list and the specification item list and generates a report in the chosen output format.

API users select reporters via their name as strings. This allows plugging in custom reporters in a loosely coupled fashion.

Exporters

The exporter transforms the internal representation of specification items into the desired target format (e.g. Markdown).

API users select exporters via their name as strings.

Runtime View

Import

Depending on the source format a variety of importers takes care of reading the input specification items. Each importer emits events which an import event listener consumes.

Common parts of the import like filtering out unnecessary items or attributes are handled by the listener.

Selective Artifact Type Import

The most resource-friendly way to enable partial tracing is to ignore unnecessary data during import. This way less memory is used up and all subsequent steps are faster.

Filtering by Artifact Types During Import

dsn~filtering-by-artifact-types-during-import~1

When OFT is configured to restrict inclusion to one or more artifact types the specification list builder imports the following elements only if they match at least one of the configured types:

  1. "Needs coverage" markers
  2. Specification items as a whole
  3. Links covering items with this artifact type
  4. Dependencies to item with this artifact type

Covers:

  • req~include-only-artifact-types~1

Needs: impl, utest, itest

Filtering by Tags During Import

dsn~filtering-by-tags-during-import~1

The specification list builder can be configured to import a specification item only if at least one of its tags is contained in the configured set of tags.

Covers:

  • req~include-items-where-at-least-on-tag-matches~1

Needs: impl, utest, itest

Filtering by Tags or no Tags During Import

dsn~filtering-by-tags-or-no-tags-during-import~1

The specification list builder can be configured to import a specification item only if it either has no tags or at least one of its tags is contained in the configured set of tags.

Covers:

  • req~include-items-that-do-not-have-tags-or-where-at-least-one-tag-matches~1

Needs: impl, utest, itest

Tracing

Tracing Needed Coverage

dsn~tracing.needed-coverage-status~1

The linker component iterates over all needed artifact types of all specification items and determines if and which coverage exists for each.

Comment: Note that the linker only takes care of swallow coverage. Deep coverage is determined by the tracer component.

Covers:

  • req~tracing.outgoing-coverage-link-status~1

Needs: utest, impl

Outgoing Coverage Link Status

dsn~tracing.outgoing-coverage-link-status~3

The linker component determines the coverage status of the outgoing link between the provider item and the requester item.

The possible results are:

  1. Covers: link points to a specification item which wants this coverage
  2. Outdated: link points to a specification item which has a higher revision number
  3. Predated: link points to a specification item which has a lower revision number
  4. Ambiguous: link points to a specification item that has duplicates
  5. Orphaned: link is broken - there is no matching coverage requester
  6. Unwanted: coverage provider has an artifact type the provider does not want

Covers:

  • req~tracing.outgoing-coverage-link-status~1

Needs: utest, impl

Incoming Coverage Link Status

dsn~tracing.incoming-coverage-link-status~1

The linker component determines the coverage status of the incoming link between the requester item and the provider item.

The possible results are:

  1. Covered shallow: coverage provider for a required coverage exists
  2. Covered unwanted: coverage provider covers an artifact type the requester does not want
  3. Covered predated: coverage provider covers a higher revision number than the requester has
  4. Covered outdated: coverage provider covers a lower revision number than the requester has

Covers:

  • req~tracing.incoming-coverage-link-status~1

Needs: impl, utest

Deep Coverage

dsn~tracing.deep-coverage~1

The Linked Specification Item declares itself covered deeply if this item - and all items it needs coverage from - are covered recursively.

Covers:

  • req~tracing.deep-coverage~1

Needs: impl, utest

Duplicate Items

dsn~tracing.tracing.duplicate-items~1

The tracer marks a specification item as a duplicate if other items with an identical specification item ID exist.

Covers:

  • req~tracing.duplicate-items~1

Needs: impl, utest

Defect Items

dsn~tracing.defect-items~2

The tracer marks a specification item as defect if the following criteria apply to the item

has duplicates
or (not rejected
    and (any outgoing coverage link has a different status than "Covers"
         or not covered deeply
        )
   )

Covers:

  • req~tracing.defect-items~2

Needs: impl, utest

Link Cycle

dsn~tracing.link-cycle~1

The tracer detects cycles in links between Linked Specification Items.

Covers:

  • req~tracing.link-cycle~1

Needs: impl, utest

Tracing Reports

Plain Text Report

Plain Text Report Summary

dsn~reporting.plain-text.summary~2

The summary in the plain text report includes:

  • Result status
  • Total number of specification items
  • Total number of specification items that are defect (if any)

Covers:

  • req~reporting.plain-text.summary~2

Needs: impl, utest

Plain Text Report Specification Item Overview

dsn~reporting.plain-text.specification-item-overview~2

An item summary consist in the plain text report includes

  1. Status
  2. Number of broken incoming links
  3. Total number of incoming links
  4. Number of broken outgoing links
  5. Total number of outgoing links
  6. Number of duplicates (not including this item)
  7. ID
  8. Status (unless "approved")
  9. Artifact types indicating coverage

Covers:

  • req~reporting.plain-text.specification-item-overview~2

Needs: impl, utest

Plain Text Report Link Details

dsn~reporting.plain-text.link-details~1

The link detail section shows for all links of a specification item:

  1. Incoming / Outgoing as arrow
  2. Link status as symbol
  3. ID of the specification item on the other end of the link

Covers:

  • req~reporting.plain-text.link-details~1

Needs: impl, utest

Plain Text Report Contains Specification Item Origin

dsn~reporting.plain-text.specification-item-origin~1

If enabled, the plain text report shows the origin of a specification item

  • for files: <absolute path to file>:<line number>

Rationale:

This format is recognized by most IDEs and automatically turned into a link in the IDE's console.

Covers:

  • req~reporting.requirement-origin~1

Needs: impl, utest

Plain Text Report Link Contains Specification Item Origin

dsn~reporting.plain-text.linked-specification-item-origin~1

If enabled, the links in the plain text report show the origin of a specification item

  • for files: <absolute path to file>:<line number>

Rationale:

This format is recognized by most IDEs and automatically turned into a link in the IDE's console.

Covers:

  • req~reporting.requirement-origin~1

Needs: impl, utest

Plain Text Report ANSI Color

dsn~reporting.plain-text.ansi-color~1

The plain text report uses ANSI escape sequences to color the output.

Covers:

  • req~colored-plain-text-report~1

Needs: impl, utest

Plain Text Report ANSI Font Style

dsn~reporting.plain-text.ansi-font-style~1

The plain text report uses ANSI escape sequences to modify the font style of the output.

Covers:

  • req~colored-plain-text-report~1
  • req~monochrome-plain-text-report-with-font-style~1

Needs: impl, utest

HTML Report

HTML Report Inlines CSS

dsn~reporting.html.inline_css~1

OFT inlines the cascading style sheet (CSS) into the HTML report.

Covers:

  • req~reporting.html.single_file~1

Needs: impl, itest

HTML Reports Allows Configuring Details Display Status

dsn~reporting.html.details-display~1

OFT allows configuring the specification item detail section display status (expanded or collapsed). Default is collapsed.

Covers:

Needs: impl, utest

HTML Report Escapes HTML Tags

dsn~reporting.html.escape-html~1

OFT escapes characters < and > when rendering the following parts of a specification item to report:

  • Title
  • Description
  • Rationale
  • Comment

Rationale:

  • This avoids generating invalid HTML when the Markdown contains text like <section>.
  • Other parts of a specification item (e.g. item ID) don't allow these characters and don't need escaping.

Covers:

Needs: impl, utest

Requirement Format Conversion

ReqM2 Export

dsn~conversion.reqm2-export~1

OFT exports to ReqM2's "SpecObject" format.

Comment: The ReqM2 format is specified in the ReqM2 handbook by Elektrobit.

Covers:

  • req~conversion.reqm2-export~1

Needs: impl, itest

HTML Report Contains Specification Item Origin

dsn~reporting.html.specification-item-origin~1

If enabled, the HTML report shows the origin of a specification item as an HTML link pointing to the source.

Covers:

  • req~reporting.requirement-origin~1

Needs: impl, utest

HTML Report Link Contains Specification Item Origin

dsn~reporting.html.linked-specification-item-origin~1

If enabled, the links in the plain text report show the origin of a specification item as an HTML link pointing to the source.

Covers:

  • req~reporting.requirement-origin~1

Needs: impl, utest

Deployment View

Concepts

Data Structures

Internal Data Structures

Specification Item

dsn~specification-item~3

A SpecificationItem consists of the following parts:

  • ID (SpecificationItemId)
  • Title (String, optional)
  • Status (Enum, optional)
  • Description (String, optional)
  • Rationale (String, optional)
  • Comment (String, optional)
  • Source file + line (String, int, optional)
  • Covers (List of SpecificationItemId, optional)
  • Depends (List of SpecificationItemId, optional)
  • Needs (List of String, optional)
  • Tags (List of String, optional)
  • Forwards (boolean, internal)

Comment:

See req~forwarding_needed_coverage~1 for an explanation of the "forwards" fields meaning.

Covers:

  • req~specification-item~2
  • req~forwarding_needed_coverage~1

Needs: impl, utest

Linked Specification Item

dsn~linked-specification-item~1

A LinkedSpecificationItem is a container for a SpecificationItem that is enriched with references to other LinkedSpecificationItems.

Rationale: This allows navigating between specification items.

Covers:

  • req~specification-item~2

Needs: impl, utest

Specification Item ID

dsn~specification-item-id~1

A SpecificationItemId consists of:

  • Artifact type (String)
  • name (String)
  • revision (number)

Covers:

  • req~specification-item~2

Needs: impl, utest

Markdown-style Structures

Markdown Specification Item ID Format

dsn~md.specification-item-id-format~3

A requirement ID has the following format

requirement-id = type "~" id "~" revision

type = 1*ALPHA

id = id-fragment *("." id-fragment)

id-fragment = UNICODE_ALPHA *(UNICODE_ALPHA / DIGIT / "_" / "-")

revision = 1*DIGIT

Rationale:

  • The ID may contain unicode letters to allow naming requirements using non-ASCII characters. This makes linking in formats like Markdown or HTML clean and easy.
  • Requirement type and revision must be immediately recognizable from the requirement ID.
  • The built-in revision number makes links break if a requirement is updated - a desired behavior.

Comment:

Note that the artifact type is integral part of the ID. That means that dsn~my-requirement~1 is something completely different then utest~my-requirement~1. One of the benefits of making the artifact type mandatory part of the ID is that this allows for typical coverage chains like.

req~my-requirement~2 -> dsn~my-requirement~4 -> impl~my-requirement~4

Otherwise users would be forced to invent different names for each link in the chain.

Covers:

  • req~markdown-standard-syntax~1

Needs: impl, utest

Markdown Specification Item Title

dsn~md.specification-item-title~1

If a Markdown title directly precedes a specification item ID, then the Markdown title is used as title for the specification item.

Rationale:

Markdown titles show up in the outline and are a natural way of defining a requirement title.

Covers:

  • req~markdown-standard-syntax~1
  • req~markdown-outline-readable~1

Needs: impl, utest

Markdown Requirement References

dsn~md.requirement-references~1

In Markdown specification item references have the following format:

reference = (plain-reference / url-style-link)

plain-reference = requirement-id

url-style-link = "[" link-text "]" "(" "#" requirement-id ")"

Covers:

  • req~markdown-standard-syntax~1

Needs: impl, utest

Markdown "Covers" list

dsn~md.covers-list~1

The Markdown Importer supports the following format for links that cover a different specification item.

covers-list = covers-header 1*(LINEBREAK covers-line)

covers-header = "Covers:" *WSP

covers-line = *WSP "*" *WSP reference

Only one traced reference per line is supported. Any optional text after the reference is ignored if it is separated by at least one whitespace character

Rationale:

Defining a link should be as natural and simple as possible in Markdown. It must also be rendered correctly by a regular Markdown renderer without modifications. Embedding links in lists to define the relationship looks nice and is language independent.

Covers:

  • req~markdown-standard-syntax~1

Needs: impl, utest

Markdown "Depends" List

dsn~md.depends-list~1

The Markdown Importer supports the following format for links to a different specification item which the current depends on.

depends-list = depends-header 1*(LINEBREAK depends-line)

depends-header = "Depends:" *WSP

depends-line = *WSP "*" *WSP reference

Only one traced reference per line is supported. Any optional text after the reference is ignored if it is separated by at least one whitespace character

Rationale:

Defining a link should be as natural and simple as possible in Markdown. It must also be rendered correctly by a regular Markdown renderer without modifications. Embedding links in lists to define the relationship looks nice and is language independent.

Covers:

  • req~markdown-standard-syntax~1

Needs: impl, utest

Markdown "Needs" List

dsn~md.needs-coverage-list~1

The Markdown Importer supports the following list format for defining the list of artifact types that are needed to fully cover the current specification item.

needs-list = needs-header 1*(LINEBREAK depends-line)

needs-header = "Needs:" *WSP

needs-line = *WSP "*" *WSP reference

Rationale:

This alternative style of the "needs" list provides backward compatibility to Elektrobit's legacy requirement enhanced Markdown format.

Covers:

  • req~markdown-standard-syntax~1

Needs: impl, utest

Markdown "Needs" List

dsn~md.needs-coverage-list-single-line~2

The Markdown Importer supports the following format for defining the list of artifact types that are needed to fully cover the current specification item.

needs-list = "Needs:" *WSP reference *("," *WSP reference)

Rationale:

Unlike references to other requirements, artifact types are usually very short, so it is visually beneficial to use a compact style with a comma separated list in a single line.

Covers:

  • req~markdown-standard-syntax~1

Needs: impl, utest

Markdown Artifact Forwarding Notation

dsn~md.artifact-forwarding-notation~1

The Markdown Importer supports forwarding required coverage from one artifact type to one or more different artifact types using the following notation.

artifact-need-redirection = skipped-artifact-type *WSP "-->" *WSP target-artifact-list
    *WSP ":" *WSP original-requirement-id
    
skipped-artifact-type = artifact-type

target-artifact-list = artifact-type *("," *WSP artifact-type)
    
original-requirement-id = requirement-id

The following example shows an architectural specification item that forwards the needed coverage directly to the detailed design and an integration test:

arch --> dsn, itest : req~skip-this-requirement~1

Covers:

  • req~artifact-type-forwarding-in-markdown~1

Needs: impl, utest

Coverage Tag Format

Full Coverage Tag Format

dsn~import.full-coverage-tag~1

OFT imports coverage tags in the full tag format:

full-tag = "[" *WSP reference "->" requirement-id "]"

Covers:

  • req~import.full-coverage-tag-format~1

Needs: impl, utest

Full Coverage Tag Format With Needed Coverage

dsn~import.full-coverage-tag-with-needed-coverage~1

OFT imports coverage tags in the full tag format with a list of required/needed artifact types:

full-tag-with-needed-coverage = "[" *WSP reference *WSP "->" *WSP requirement-id
*WSP ">>" *WSP artifact-type *WSP *("," *WSP artifact-type) "]"

Rationale:

The Tag importer is the catch all solution for all file formats that don't have a dedicated importer. We want to allow specification items imported via Tag importer to be intermediate nodes in the specification tree, instead of limiting them to leaves in the specification tree. Because leaves can only cover other specification items, but not require coverage.

Especially when used for design document files like UML models, requiring coverage is useful.

Covers:

  • req~import.full-coverage-tag-format~1

Needs: impl, utest

Full Coverage Tag Format Allows Specifying a Revision

dsn~import.full-coverage-tag-with-revision~1

OFT imports full coverage tags with an optional revision:

full-tag-with-revision = "[" *WSP reference "~" "~" revision "->" requirement-id "]"

full-tag-with-revision-with-needed-coverage = "[" *WSP reference "~" "~" revision *WSP "->" *WSP requirement-id
*WSP ">>" *WSP artifact-type *WSP *("," *WSP artifact-type) "]"

Rationale:

Specifying an explicit revision in coverage tags allows incrementing the revision when the implementation changes. Without this, OFT would always assign the default revision 0.

Covers:

  • req~import.full-coverage-tag-format~1

Needs: impl, utest

Full Coverage Tag Format Allows Specifying Name and Revision

dsn~import.full-coverage-tag-with-name-and-revision~1

OFT imports full coverage tags with optional name and revision:

full-tag-with-name-and-revision =
    "[" *WSP covering-artifact-type "~" covering-name "~" covering-revision
    "->" covered-requirement-id "]"

full-tag-with-name-and-revision-with-needed-coverage =
    "[" *WSP covering-artifact-type "~" xxx "~" revision *WSP
    "->" *WSP requirement-id *WSP
    ">>" *WSP artifact-type *WSP *("," *WSP artifact-type) "]"

Rationale:

Specifying an explicit name in coverage tags allows overriding the auto-generated name.

Covers:

  • req~import.full-coverage-tag-format~1

Needs: impl, utest

Coverage Tag With Needed Coverage Generates Readable Names

dsn~import.full-coverage-tag-with-needed-coverage-readable-names~1

OFT generates readable names without hash code ID for tags with needed coverage.

Rationale:

When you need to cover these items it's important that the name is predictable and does not change e.g. when the file changes.

Covers:

  • req~import.full-coverage-tag-format~1

Needs: impl, utest

Short Coverage Tag Format

dsn~import.short-coverage-tag~1

OFT imports coverage tags in the short tag format:

short-tag = "[" "[" *WSP reference ":" *revision "]" "]"

During import of short tags OFT requires the following configuration:

  • Path from which to import the tags
  • Artifact type of the tags
  • Artifact type of the covered specification item
  • Name prefix of the covered specification item. The prefix is optional, default value: project name "."

Covers:

  • req~import.short-coverage-tag-format~1

Needs: impl, utest

User Interface

CLI Command Selection

dsn~cli.command-selection~1

The CLI expects one of the following commands as first unnamed command line parameter:

command = "trace" / "convert"

Covers:

  • req~cli.tracing.command~1
  • req~cli.conversion.command~1

Needs: impl, itest

Common

Input File Selection

dsn~cli.input-file-selection~1

The CLI accepts the following two variants for defining input files:

  • A list of files
  • A list of directories

In both cases relative and absolute paths are accepted. "Relative" means in relation to the current working directory.

Covers:

  • req~cli.input-selection~1

Needs: impl, itest

Input Directory Recursive Traversal

dsn~input-directory-recursive-traversal~1

The Importer reads all requirement input files from all input directories recursively.

Covers:

  • req~cli.input-directory-selection~1

Needs: impl, itest

Default Input

dsn~cli.default-input~1

If the user does not specify any inputs as CLI parameters, the CLI uses the current working directory as default input.

Covers:

  • req~cli.default-input~1

Needs: impl, itest

Newline Format

dsn~newline-format~1

The CLI accepts one of the following newline formats:

new-line-format = "unix" / "windows"

Rationale:

When users work together in teams where the team members use different platforms, configuring the newline helps the team to set a common standard.

Covers:

  • req~cli.newline-format~1

Needs: impl, itest

Default Newline Format

dsn~cli.default-newline-format~1

If the user does not specify the newline format as parameter, the exporter uses the native newline format of the platform OFT is executed on.

Covers:

  • req~cli.default-newline-format~1

Needs: impl, itest

Requirement Tracing

Tracing Output Format

dsn~cli.tracing.output-format~1

The CLI accepts one of the following requirement tracing report formats as parameter:

report-formats = "plain"

Covers:

  • req~cli.tracing.output-format~1

Needs: impl, itest

Default Tracing Output Format

dsn~cli.tracing.default-format~1

The CLI uses plain text as requirement tracing report format if none is given as a parameter.

Covers:

  • req~cli.tracing.default-output-format~1

Needs: impl, utest

Tracing Exit Status

dsn~cli.tracing.exit-status~1

The return value of the OFT executable is:

  • 0 tracing was successful
  • 1 tracing ran successfully, but the tracing result is negative

Covers:

  • req~cli.tracing.exit-status~1

Needs: impl, itest

Requirement Format Conversion

Conversion Output Format

dsn~cli.conversion.output-format~1

The CLI accepts one of the following export formats as parameter:

export-formats = "reqm2"

Covers:

  • req~cli.conversion.output-format~1

Needs: impl, itest

Default Conversion Output Format

dsn~cli.conversion.default-output-format~1

The CLI uses ReqM2 as export format if none is given as a parameter.

Covers:

  • req~cli.conversion.default-output-format~1

Needs: impl, itest, utest

Design Decisions

How do we Implement the Command Line Interpreter

dsn~reflection-based-cli~1

OFT got its own simple command line interpreter that uses reflection to feed the command line arguments to a receiver object.

Rationale:

One of the design goal of OFT is that it works without external runtime dependencies except for the Java Standard API. So taking an existing CLI was no option. Using reflection allows the CLI user to implement the receiver as a POJO. No annotations are necessary.

Covers:

  • req~cli.tracing.command~1
  • req~cli.conversion.command~1

Why is This Architecture Relevant?

Exchanging the CLI later takes considerable effort.

Alternatives Considered

  • No CLI (plain argument list) - not flexible enough
  • External CLI - breaks design goal

How do we Clean Imported Multi-line Text Elements

dsn~cleaning-imported-multi-line-text-elements~1

The ImportEventListeners do the following clean-up steps in multi-line text elements like the description:

  • Trimming (removing leading and trailing spaces of the whole text -- no individual lines.)

Rationale:

This way we do cleanup in a central place and don't produce code duplication. Extra clean-up in the importers is still possible, but basics like trimming need to be handled in common code.

Needs: impl, utest

Why is This Architecture Relevant?

Authors of importers need to be able to rely on these cleanups being done centrally, so that they don't have to implement them themselves.

Alternatives Considered

Clean-up in every importer individually. That was the case up to and including OFT 3.7.1 which turned out to make the importer more complex than necessary.

Bibliography

The following documents or are referenced in this specification.

Specifications

Web Sites