From c2733fff89e351685f5e1fe7017f1898b51730b1 Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Thu, 20 Nov 2025 20:31:10 +0000 Subject: [PATCH 1/4] Add decision log and functional requirements documentation for Chronicle Values --- AGENTS.md | 76 ++++++++++++--- README.adoc | 2 + pom.xml | 2 +- src/main/adoc/decision-log.adoc | 93 ------------------- src/main/docs/decision-log.adoc | 73 ++++++++++++++- src/main/docs/functional-requirements.adoc | 40 ++++++++ .../{adoc => docs}/project-requirements.adoc | 6 ++ src/main/docs/systemProperties.adoc | 26 ++++++ 8 files changed, 209 insertions(+), 109 deletions(-) delete mode 100644 src/main/adoc/decision-log.adoc create mode 100644 src/main/docs/functional-requirements.adoc rename src/main/{adoc => docs}/project-requirements.adoc (96%) create mode 100644 src/main/docs/systemProperties.adoc diff --git a/AGENTS.md b/AGENTS.md index de6b15514..cae254877 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,9 +10,10 @@ LLM-based agents can accelerate development only if they respect our house rules | Requirement | Rationale | |--------------|-----------| -| **British English** spelling (`organisation`, `licence`, *not* `organization`, `license`) except technical US spellings like `synchronized` | Keeps wording consistent with Chronicle's London HQ and existing docs. See the University of Oxford style guide for reference. | -| **ASCII-7 only** (code-points 0-127). Avoid smart quotes, non-breaking spaces and accented characters. | ASCII-7 survives every toolchain Chronicle uses, incl. low-latency binary wire formats that expect the 8th bit to be 0. | -| If a symbol is not available in ASCII-7, use a textual form such as `micro-second`, `>=`, `:alpha:`, `✓`. This is the preferred approach and Unicode must not be inserted. | Extended or '8-bit ASCII' variants are *not* portable and are therefore disallowed. | +| **British English** spelling (`organisation`, `licence`, *not* `organization`, `license`) except technical US spellings like `synchronized` | Keeps wording consistent with Chronicle's London HQ and existing docs. See the [University of Oxford style guide](https://www.ox.ac.uk/public-affairs/style-guide) for reference. | +| **ISO-8859-1** (code-points 0-255). Avoid smart quotes, non-breaking spaces and accented characters. | ISO-8859-1 survives every toolchain Chronicle uses. | +| If a symbol is not available in ISO-8859-1, use a textual form such as `>=`, `:alpha:`, `:yes:`. This is the preferred approach and Unicode must not be inserted. | Extended or '8-bit ASCII' variants are *not* portable and are therefore disallowed. | +| Tools to check ASCII compliance include `iconv -f ascii -t ascii` and IDE settings that flag non-ASCII characters. | These help catch stray Unicode characters before code review. | ## Javadoc guidelines @@ -26,25 +27,45 @@ noise and slows readers down. | Prefer `@param` for *constraints* and `@throws` for *conditions*, following Oracle's style guide. | Pad comments to reach a line-length target. | | Remove or rewrite autogenerated Javadoc for trivial getters/setters. | Leave stale comments that now contradict the code. | -The principle that Javadoc should only explain what is *not* manifest from the signature is well-established in the -wider Java community. +The principle that Javadoc should only explain what is *not* manifest from the +signature is well-established in the wider Java community. + +Inline comments should also avoid noise. The following example shows the +difference: + +```java +// BAD: adds no value +int count; // the count + +// GOOD: explains a subtlety +// count of messages pending flush +int count; +``` ## Build & test commands -Agents must verify that the project still compiles and all unit tests pass before opening a PR: +Agents must verify that the project still compiles and all unit tests pass before opening a PR. Running from a clean checkout avoids stale artifacts: ```bash # From repo root -mvn -q verify +mvn -q clean verify ``` +The command should exit with code `0` to indicate success. ## Commit-message & PR etiquette -1. **Subject line <= 72 chars**, imperative mood: "Fix roll-cycle offset in `ExcerptAppender`". +1. **Subject line <= 72 chars**, imperative mood: Fix roll-cycle offset in `ExcerptAppender`. 2. Reference the JIRA/GitHub issue if it exists. 3. In *body*: *root cause -> fix -> measurable impact* (latency, allocation, etc.). Use ASCII bullet points. 4. **Run `mvn verify`** again after rebasing. +### When to open a PR + +* Open a pull request once your branch builds and tests pass with `mvn -q clean verify`. +* Link the PR to the relevant issue or decision record. +* Keep PRs focused: avoid bundling unrelated refactoring with new features. +* Re-run the build after addressing review comments to ensure nothing broke. + ## What to ask the reviewers * *Is this AsciiDoc documentation precise enough for a clean-room re-implementation?* @@ -53,10 +74,18 @@ mvn -q verify * Does the commit point back to the relevant requirement or decision tag? * Would an example or small diagram help future maintainers? +### Security checklist (review **after every change**) + +**Run a security review on *every* PR**: Walk through the diff looking for input validation, authentication, authorisation, encoding/escaping, overflow, resource exhaustion and timing-attack issues. + +**Never commit secrets or credentials**: tokens, passwords, private keys, TLS materials, internal hostnames, Use environment variables, HashiCorp Vault, AWS/GCP Secret Manager, etc. + +**Document security trade-offs**: Chronicle prioritises low-latency systems; sometimes we relax safety checks for specific reasons. Future maintainers must find these hot-spots quickly, In Javadoc and `.adoc` files call out *why* e.g. "Unchecked cast for performance - assumes trusted input". + ## Project requirements -See the [Decision Log](src/main/adoc/decision-log.adoc) for the latest project decisions. -See the [Project Requirements](src/main/adoc/project-requirements.adoc) for details on project requirements. +See the [Decision Log](src/main/docs/decision-log.adoc) for the latest project decisions. +See the [Project Requirements](src/main/docs/project-requirements.adoc) for details on project requirements. ## Elevating the Workflow with Real-Time Documentation @@ -84,7 +113,7 @@ This tight loop informs the AI accurately and creates immediate clarity for all When using AI agents to assist with development, please adhere to the following guidelines: -* **Respect the Language & Character-set Policy**: Ensure all AI-generated content follows the British English and ASCII-7 guidelines outlined above. +* **Respect the Language & Character-set Policy**: Ensure all AI-generated content follows the British English and ISO-8859-1 guidelines outlined above. Focus on Clarity: AI-generated documentation should be clear and concise and add value beyond what is already present in the code or existing documentation. * **Avoid Redundancy**: Do not generate content that duplicates existing documentation or code comments unless it provides additional context or clarification. * **Review AI Outputs**: Always review AI-generated content for accuracy, relevance, and adherence to the project's documentation standards before committing it to the repository. @@ -122,8 +151,7 @@ Date:: YYYY-MM-DD Context:: * What is the issue that this decision addresses? * What are the driving forces, constraints, and requirements? -Decision Statement:: -* What is the change that is being proposed or was decided? +Decision Statement :: What is the change that is being proposed or was decided? Alternatives Considered:: * [Alternative 1 Name/Type]: ** *Description:* Brief description of the alternative. @@ -151,7 +179,7 @@ Notes/Links:: Do not rely on indentation for list items in AsciiDoc documents. Use the following pattern instead: ```asciidoc -section:: Top Level Section +section :: Top Level Section (Optional) * first level ** nested level ``` @@ -159,3 +187,23 @@ section:: Top Level Section ### Emphasis and Bold Text In AsciiDoc, an underscore `_` is _emphasis_; `*text*` is *bold*. + +### Section Numbering + +Use automatic section numbering for all `.adoc` files. + +* Add `:sectnums:` to the document header. +* Do not prefix section titles with manual numbers to avoid duplication. + +```asciidoc += Document Title +Chronicle Software +:toc: +:sectnums: +:lang: en-GB +:source-highlighter: rouge + +The document overview goes here. + +== Section 1 Title +``` diff --git a/README.adoc b/README.adoc index 48a3f45ee..02e02e47f 100644 --- a/README.adoc +++ b/README.adoc @@ -3,6 +3,8 @@ Chronicle Software :css-signature: demo :toc: macro :toclevels: 3 +:lang: en-GB +:source-highlighter: rouge image:https://maven-badges.herokuapp.com/maven-central/net.openhft/chronicle-values/badge.svg[link=https://maven-badges.herokuapp.com/maven-central/net.openhft/chronicle-values] image:https://javadoc.io/badge2/net.openhft/chronicle-values/javadoc.svg[link=https://www.javadoc.io/doc/net.openhft/chronicle-values/latest/index.html] diff --git a/pom.xml b/pom.xml index 770dc71ff..a617094a0 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ net.openhft third-party-bom - 3.27ea5 + 3.27ea7 pom import diff --git a/src/main/adoc/decision-log.adoc b/src/main/adoc/decision-log.adoc deleted file mode 100644 index bad15bfa4..000000000 --- a/src/main/adoc/decision-log.adoc +++ /dev/null @@ -1,93 +0,0 @@ -= Decision Log: Chronicle Libraries Usage - -This log records key decisions regarding the selection and use of Chronicle libraries, based on the "A Deep Dive into Chronicle Values versus Chronicle Bytes for Project Requirements Analysis" report. -Identifiers use the `CV` project scope and relevant Nine-Box tags. - -The requirement tags referenced here originate from link:project-requirements.adoc[Project Requirements]. - -== [CV-FN-032] Adoption of Chronicle Values for Structured, Fixed-Size Data - -* Date: 2025-05-26 -* Context: -** Need for a type-safe, efficient, and developer-friendly way to represent and manipulate data with well-defined, fixed-size schemas (e.g., financial messages, event payloads). -** Desire to minimize GC impact and integrate smoothly with other Chronicle libraries like Map and Queue. -** Requirements: CV-FN-001, CV-FN-003, CV-FN-007, CV-FN-008, CV-NFP-011, CV-NFP-012, CV-UX-026. -* Decision Statement: -** Chronicle Values *will be adopted* as the primary mechanism for modeling and accessing structured, fixed-size data, particularly when type safety, developer convenience, and integration with Chronicle Map/Queue are priorities. -* **Alternatives Considered:** -** _Direct use of Chronicle Bytes for all structured data_: -*** *Description:* Manually managing offsets and data interpretation within Chronicle Bytes buffers for all structured data. -*** *Pros:* Maximum control over memory layout; potentially the absolute lowest latency if hand-optimized perfectly. -*** *Cons:* Higher development complexity; error-prone (offset miscalculations, type misinterpretations); poor type safety at compile time; more boilerplate code. -** _Using third-party serialization libraries (e.g., Protocol Buffers, Avro) on top of Chronicle Bytes_: -*** *Description:* Employing external libraries for defining structures and serializing/deserializing them into/from Bytes buffers. -*** *Pros:* Mature schema definition and evolution capabilities; language interoperability (for some). -*** *Cons:* Potential performance overhead compared to direct flyweight access; may not offer "zero-deserialization" in the same way as Values flyweights; less direct integration with Chronicle ecosystem idioms. -** _Exclusive use of on-heap Java objects (POJOs) with manual serialization to Chronicle Bytes_: -*** *Description:* Using standard POJOs and writing custom serialization logic to/from Bytes. -*** *Pros:* Familiar development paradigm for POJOs. -*** *Cons:* Significant GC pressure if many objects are created; manual serialization is complex and error-prone; loses off-heap benefits for data-in-flight/at-rest unless meticulously managed. -* **Rationale for Decision:** -** Chronicle Values provides a strong balance of performance (via off-heap flyweights, "zero-deserialization"), type safety (compile-time checks via interfaces), and developer convenience (generated accessors, annotation-driven configuration) for its target use case. -** It directly addresses the need for fixed-layout data representation and seamless integration with Chronicle Map/Queue. -** The flyweight pattern minimizes object churn and GC impact, aligning with performance goals. -* **Impact & Consequences:** -** Build system must integrate Chronicle Values annotation processor (CV-OPS-024). -** Development team requires training on Chronicle Values interface definition, annotations, and the distinction between flyweights and heap beans (CV-UX-027). -** Improved developer productivity and reduced errors for tasks involving supported structured data. -** Enhanced performance due to reduced GC pressure and efficient data access. - -== [CV-FN-033] Adoption of Chronicle Bytes for Low-Level and Unstructured Data - -* Date: 2025-05-26 -* Context: -** Need for direct, low-level manipulation of byte sequences, implementation of custom serialization/deserialization, parsing/constructing binary network protocols, and handling dynamic or unstructured data. -** Requirements for maximum control over memory and ultimate performance potential in specific scenarios. -** Requirements: CV-FN-002, CV-NFP-009, CV-NFO-016, CV-NFS-018, CV-NFS-019. -* Decision Statement: -** Chronicle Bytes *will be adopted* for tasks requiring foundational byte manipulation, handling unstructured/dynamic data, achieving maximum control and performance where the overhead of higher-level abstractions is unacceptable, and as the underlying layer for other data abstractions. -* **Alternatives Considered:** -** _Exclusive use of Chronicle Values_: -*** *Description:* Attempting to model all data, including highly dynamic or unstructured data, using Chronicle Values interfaces. -*** *Pros:* Consistent use of a single higher-level API. -*** *Cons:* Not suitable for data that isn't fixed-size or well-structured at compile time; would lead to inefficient workarounds or be impossible for truly raw binary data. -** _Standard Java NIO (ByteBuffer)_: -*** *Description:* Using Java's built-in ByteBuffer for all low-level byte operations. -*** *Pros:* Part of standard Java, no external dependencies. -*** *Cons:* Generally lower performance than Chronicle Bytes; API considered less flexible (e.g., `flip()` operation, separate read/write positions not as convenient); less comprehensive support for off-heap memory management idioms like reference counting. -* **Rationale for Decision:** -** Chronicle Bytes is specifically designed for high-performance, low-level memory operations, offering superior performance and flexibility compared to standard Java NIO. -** It provides essential tools for direct memory access (on-heap and off-heap), fine-grained control over buffer state, and support for a wide array of data types. -** Its maturity and foundational role in the Chronicle ecosystem make it a reliable choice for critical low-level tasks. -* **Impact & Consequences:** -** Requires developers to have expertise in low-level Java programming, manual memory management (especially off-heap), and byte manipulation (CV-UX-027). -** Increased responsibility on developers for memory safety (bounds, resource release) (CV-RISK-031, CV-NFS-019). -** Enables highest possible performance for specific tasks. -** Provides flexibility to handle any binary data format. - -== [CV-FN-034] Strategy for Combined Usage of Chronicle Values and Bytes - -* Date: 2025-05-26 -* Context: -** Many systems require both low-level data handling (e.g., network I/O, parsing raw streams) and higher-level, typed access to structured portions of that data. -** Need to leverage the strengths of both Chronicle Bytes and Chronicle Values within the same application or data pipeline. -* Decision Statement: -** A combined approach, where Chronicle Bytes is used for raw I/O and initial processing of byte streams, and Chronicle Values flyweights are subsequently mapped over specific, structured portions of these Bytes buffers, *is endorsed* for relevant use cases. -** Data can be copied from Values flyweights to generated heap beans for interaction with standard Java components where performance is less critical. -* **Alternatives Considered:** -** _Using only Chronicle Bytes_: -*** *Description:* Manually implementing parsing and typed access for all structured data segments. -*** *Pros:* Single library dependency for byte-level tasks. -*** *Cons:* Loses benefits of type safety and developer convenience offered by Values for structured parts; more complex to maintain. -** _Using only Chronicle Values (and forcing data into its model)_: -*** *Description:* Attempting to fit all data, including initial raw streams, directly into Values interfaces, potentially by pre-processing into conforming Bytes buffers. -*** *Pros:* Single high-level API for data access. -*** *Cons:* Impractical or inefficient for truly raw/unstructured initial data stages; might add unnecessary processing steps. -* **Rationale for Decision:** -** This layered approach allows leveraging the raw power and flexibility of Chronicle Bytes for low-level I/O and parsing, and the type safety and convenience of Chronicle Values for interacting with known, structured data segments within those byte streams. -** It provides a pathway to bridge performance-critical sections (using off-heap Values flyweights) with other parts of an application that expect standard Java objects (via copying to on-heap beans). -** This synergistic use maximizes the benefits of both libraries. -* **Impact & Consequences:** -** Developers need to understand the interplay between Bytes buffers and Values flyweights, including how flyweights are mapped to specific memory regions within a Bytes instance. -** Careful management of the Bytes buffer lifecycle is crucial, as Values flyweights depend on its validity. -** Architectural patterns will need to define clear boundaries and responsibilities for when Bytes is used versus when Values is applied. diff --git a/src/main/docs/decision-log.adoc b/src/main/docs/decision-log.adoc index 9b210ff35..ab6230a40 100644 --- a/src/main/docs/decision-log.adoc +++ b/src/main/docs/decision-log.adoc @@ -1,4 +1,75 @@ -== [VAL-SPOT-301] Code-Review Profile Suppressions for Legacy Generators += Chronicle Values - Decision Log +:toc: +:lang: en-GB +:source-highlighter: rouge + +This file captures component-specific architectural and operational decisions for the Chronicle Values module. +Identifiers follow the `--NNN` pattern; numbers are unique within the `VAL` scope and link back to `CV-*` requirements where relevant. + +== Decision Index + +* link:#VAL-FN-101[VAL-FN-101 Value Interfaces as Single Source of Truth] +* link:#VAL-NFP-111[VAL-NFP-111 Constant-Size Flyweights Backed by Chronicle Bytes] +* link:#VAL-SPOT-301[VAL-SPOT-301 Code-Review Profile Suppressions for Legacy Generators] + +[[VAL-FN-101]] +== VAL-FN-101 Value Interfaces as Single Source of Truth + +Date:: 2025-11-14 +Context:: +* Chronicle Values generates constant-size flyweight accessors and on-heap bean implementations from user-defined value interfaces. +* Without a single, normative definition of fields and behaviours, schemas can drift between documentation, heap models and off-heap layouts. +* Requirements: CV-FN-001, CV-FN-003, CV-FN-004, CV-FN-007, CV-DOC-022. +Decision:: +* Treat value interfaces as the single source of truth for data layout and semantics; generated native and heap implementations must be derivable from the interface specification alone. +* Keep `net.openhft.chronicle.values.internal` classes as implementation detail; consumers interact with generated types via public APIs and helper factories (`Values.newNativeReference`, `Values.newHeapInstance`). +Alternatives:: +* Hand-written POJOs and manual serializers alongside value interfaces :: +** Pros: Maximum flexibility for special cases. +** Cons: Duplication of schema knowledge; high risk of divergence between representations. +* External schema descriptions (IDLs, separate metadata files) driving generation :: +** Pros: Potential multi-language support. +** Cons: Additional toolchain complexity; weak alignment with Chronicle Values API design centred on Java interfaces. +Rationale:: +* A single specification point keeps the mental model simple and reduces the chance of layout and behaviour drift. +* Aligns the library with its documented usage in `project-requirements.adoc` and README, where value interfaces already express structure, annotations and supported method patterns. +Impact:: +* Documentation and examples must emphasise value interfaces as the canonical schema. +* Tests for new features should target the interface level, exercising both native and heap implementations derived from the same definition. +Links:: +* link:../adoc/project-requirements.adoc[Project Requirements: Chronicle Values] +* link:../../README.adoc[Chronicle Values README] + +[[VAL-NFP-111]] +== VAL-NFP-111 Constant-Size Flyweights Backed by Chronicle Bytes + +Date:: 2025-11-14 +Context:: +* Chronicle Values advertises generation of constantly-sized flyweight accessors backed by Chronicle Bytes stores. +* Performance requirements call for predictable memory layout, low allocation rates and efficient off-heap access. +* Requirements: CV-NFP-011, CV-NFP-012, CV-NFP-013, CV-NFP-015. +Decision:: +* Maintain a constant-size layout for each value interface, using annotations such as `@MaxUtf8Length`, `@Array`, `@Range` and `@Align` to bound variable-length fields and control packing. +* Continue to back native implementations with `BytesStore` and expose `Byteable` so callers can attach flyweights to existing Bytes regions without extra allocation. +Alternatives:: +* Variable-size layouts that grow with actual field content :: +** Pros: Denser packing for sparse or rarely populated fields. +** Cons: Unpredictable offsets, slower random access and more complex schema evolution. +* Heap-only representations with on-demand serialisation to Bytes :: +** Pros: Familiar object model and tooling. +** Cons: Higher GC pressure and extra copy cost on each serialisation boundary. +Rationale:: +* Fixed layouts align with Chronicle's low-latency and off-heap design goals, keeping field offsets stable and enabling efficient CAS and alignment-sensitive operations. +* Annotation-driven configuration keeps the model expressive while preserving the constant-size property. +Impact:: +* Interface authors must supply appropriate annotations for strings, arrays and constrained numeric fields to avoid accidental layout bloat. +* Changes to annotations or method signatures can be breaking at the binary layout level and must be reviewed against persistence and IPC use cases. +Links:: +* link:../adoc/project-requirements.adoc[Project Requirements: Chronicle Values] +* link:../../README.adoc[Chronicle Values README] + +[[VAL-SPOT-301]] +== VAL-SPOT-301 Code-Review Profile Suppressions for Legacy Generators Date:: 2025-10-28 Context:: diff --git a/src/main/docs/functional-requirements.adoc b/src/main/docs/functional-requirements.adoc new file mode 100644 index 000000000..7ce62cc35 --- /dev/null +++ b/src/main/docs/functional-requirements.adoc @@ -0,0 +1,40 @@ += Chronicle Values - Functional Requirements +:toc: +:lang: en-GB +:source-highlighter: rouge + +== Purpose + +This document summarises the core functional behaviours required when using Chronicle Values alongside Chronicle Bytes. +It organises the `CV-FN-*` requirements from `src/main/adoc/project-requirements.adoc` into domains and links them to supporting verification and documentation. +The full catalogue, including non-functional, operational and risk requirements, remains in `project-requirements.adoc`. + +== Domain Overview + +[cols="1,3,3",options="header"] +|=== +| Tag range | Domain | Notes +| CV-FN-001 .. CV-FN-008 | Library selection and data modelling | When to apply Chronicle Values versus Chronicle Bytes; integration with Map and Queue. +|=== + +== Functional Requirements (CV-FN-001 .. CV-FN-008) + +[cols="1,4,3",options="header"] +|=== +| ID | Requirement (summary) | Verification and references +| CV-FN-001 | Support representation and manipulation of highly structured data with a fixed, compile-time known schema, expressed using Chronicle Values interfaces. | Examples and tests that define value interfaces, generate flyweights and exercise field access; see `project-requirements.adoc` and the Chronicle Values decision logs for usage guidance. +| CV-FN-002 | Support handling of unstructured, raw binary data or data with dynamically determined or evolving schemas via Chronicle Bytes. | Tests and examples that use Chronicle Bytes directly for raw buffers, together with documentation comparing Bytes and Values usage. +| CV-FN-003 | Provide mechanisms for defining and accessing complex, nested data structures. | Tests demonstrating nested value interfaces and corresponding flyweights; documentation in `project-requirements.adoc` on nested structures and layout annotations. +| CV-FN-004 | Support efficient inter-process communication for structured data using Chronicle Values flyweights mapped over shared memory. | Integration tests where value flyweights are shared between processes or threads via Chronicle Queue or Chronicle Map; documentation in the decision logs explaining inter-process communication patterns. +| CV-FN-005 | Support efficient inter-process communication for raw byte streams using Chronicle Bytes. | Examples and tests that use Bytes over memory-mapped files or network transports to exchange unstructured data. +| CV-FN-006 | Provide mechanisms for efficient serialisation and deserialisation between Java objects and byte representations using both Chronicle Values and Bytes. | Tests covering `BytesMarshallable` implementations and generated serialisation for values, along with any benchmarks referenced in `project-requirements.adoc`. +| CV-FN-007 | Allow data definition using Java interfaces for structured data types, with generated implementations providing flyweight access. | Annotation-processing tests and examples that compile interface definitions into generated classes or bytecode; build configuration following `CV-OPS-024`. +| CV-FN-008 | Support interaction with Chronicle Map and Chronicle Queue using efficiently represented keys and values, especially for fixed-size structured data. | Integration tests that store value-based keys or values in Chronicle Map or publish them through Chronicle Queue; documentation sections that describe mapping between Value interfaces and storage layouts. +|=== + +== Traceability + +- The authoritative definitions of `CV-FN-*` requirements are in `src/main/adoc/project-requirements.adoc`. +- Decision records in `src/main/adoc/decision-log.adoc` and `src/main/docs/decision-log.adoc` describe how Chronicle Values and Chronicle Bytes are combined to satisfy these requirements in concrete designs. +- Tests and examples that exercise Chronicle Values should reference the relevant `CV-FN-*` identifiers where practical to make requirement coverage easy to audit. + diff --git a/src/main/adoc/project-requirements.adoc b/src/main/docs/project-requirements.adoc similarity index 96% rename from src/main/adoc/project-requirements.adoc rename to src/main/docs/project-requirements.adoc index 8f78b72f7..743d81061 100644 --- a/src/main/adoc/project-requirements.adoc +++ b/src/main/docs/project-requirements.adoc @@ -1,4 +1,8 @@ = Project Requirements: Chronicle Values +:toc: +:lang: en-GB +:sectnums: +:source-highlighter: rouge This document outlines the requirements for utilizing Chronicle Values. The requirements are tagged using the company-wide Nine-Box taxonomy with the project scope `CV` (Chronicle Values). @@ -6,6 +10,8 @@ The requirements are tagged using the company-wide Nine-Box taxonomy with the pr See `AGENTS.md` for the global policy on tag usage. The Nine-Box scheme covers `FN` for functional behaviour, `NF-P`, `NF-S` and `NF-O` for performance, security and operability, `TEST` and `DOC` for testing and documentation, `OPS` for operational concerns, `UX` for user experience and `RISK` for compliance. +See link:decision-log.adoc[Decision Log: Chronicle Libraries Usage] and link:../docs/decision-log.adoc[Chronicle Values - Decision Log] for related architectural decisions. + == Functional Requirements (FN) * *CV-FN-001:* The system *must* support representation and manipulation of highly structured data with a fixed, compile-time known schema. diff --git a/src/main/docs/systemProperties.adoc b/src/main/docs/systemProperties.adoc new file mode 100644 index 000000000..83edd5090 --- /dev/null +++ b/src/main/docs/systemProperties.adoc @@ -0,0 +1,26 @@ += Chronicle Values System Properties +:toc: +:lang: en-GB +:source-highlighter: rouge + +== System Properties + +Chronicle Values has a minimal set of system properties, primarily aimed at debugging generated code. +Set each property on the Java command line using `-Dkey=value`. + +NOTE: Boolean properties are read via `Jvm.getBoolean` and follow the usual Chronicle convention where an empty value, `true` or `yes` all enable the flag. + +.System properties +[cols="2a,1,3a,2a",options="header"] +|=== +| Property Key +| Default Value +| Description +| Java Variable Name (Type) + +| `chronicle.values.dumpCode` +| `false` +| When enabled, prints the generated Java source for native and heap implementations of value interfaces to standard output during code generation. Intended for debugging generator behaviour and not for normal production use. +| Used in `Generators.generateNativeClass` and `Generators.generateHeapClass` (`boolean`) +|=== + From 42a82a105283569d15e7e32d2307d671cf2753d0 Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Thu, 20 Nov 2025 20:31:22 +0000 Subject: [PATCH 2/4] Enhance error handling and improve code readability in various models --- .../openhft/chronicle/values/ArrayFieldModel.java | 3 ++- .../chronicle/values/BooleanFieldModel.java | 14 ++++++++++---- .../java/net/openhft/chronicle/values/Enums.java | 2 +- .../chronicle/values/FloatingFieldModel.java | 4 ++-- .../net/openhft/chronicle/values/Generators.java | 4 ++-- .../chronicle/values/MyJavaFileManager.java | 6 +++--- .../values/PrimitiveBackedHeapMemberGenerator.java | 6 ++++-- .../chronicle/values/SimpleURIClassObject.java | 3 ++- .../java/net/openhft/chronicle/values/Utils.java | 2 ++ .../net/openhft/chronicle/values/ValueModel.java | 3 ++- 10 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/openhft/chronicle/values/ArrayFieldModel.java b/src/main/java/net/openhft/chronicle/values/ArrayFieldModel.java index 512c766dc..356d33133 100644 --- a/src/main/java/net/openhft/chronicle/values/ArrayFieldModel.java +++ b/src/main/java/net/openhft/chronicle/values/ArrayFieldModel.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.NotNull; import java.lang.reflect.Method; +import java.util.Objects; import static net.openhft.chronicle.values.Utils.roundUp; @@ -50,7 +51,7 @@ public class ArrayFieldModel extends FieldModel { private MemberGenerator nativeGenerator; public ArrayFieldModel(ScalarFieldModel elemModel) { - this.elemModel = elemModel; + this.elemModel = Objects.requireNonNull(elemModel); } /** diff --git a/src/main/java/net/openhft/chronicle/values/BooleanFieldModel.java b/src/main/java/net/openhft/chronicle/values/BooleanFieldModel.java index 120619f50..be7f777f4 100644 --- a/src/main/java/net/openhft/chronicle/values/BooleanFieldModel.java +++ b/src/main/java/net/openhft/chronicle/values/BooleanFieldModel.java @@ -120,8 +120,8 @@ void generateArrayElementSet( arrayElementSet(arrayFieldModel, valueBuilder, methodBuilder, "", ""); } - private void arrayElementSet - (ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, + private void arrayElementSet( + ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String readType, String writeType) { int arrayBitOffset = valueBuilder.model.fieldBitOffset(arrayFieldModel); methodBuilder.addStatement("int bitOffset = $L + index", arrayBitOffset); @@ -335,8 +335,14 @@ MemberGenerator nativeGenerator() { @Override MemberGenerator createHeapGenerator() { - return new PrimitiveBackedHeapMemberGenerator(this) { + return new BooleanHeapMemberGenerator(this); + } - }; + private static final class BooleanHeapMemberGenerator + extends PrimitiveBackedHeapMemberGenerator { + + private BooleanHeapMemberGenerator(FieldModel fieldModel) { + super(fieldModel); + } } } diff --git a/src/main/java/net/openhft/chronicle/values/Enums.java b/src/main/java/net/openhft/chronicle/values/Enums.java index def9dbf38..e564a5ce5 100644 --- a/src/main/java/net/openhft/chronicle/values/Enums.java +++ b/src/main/java/net/openhft/chronicle/values/Enums.java @@ -44,7 +44,7 @@ public static > E[] getUniverse(Class enumType) { //noinspection unchecked return Jvm.uncheckedCast(getUniverse.invoke(null, enumType)); } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); + throw new IllegalStateException("Unable to access enum constants", e); } } diff --git a/src/main/java/net/openhft/chronicle/values/FloatingFieldModel.java b/src/main/java/net/openhft/chronicle/values/FloatingFieldModel.java index 7659a79b4..bb4854f16 100644 --- a/src/main/java/net/openhft/chronicle/values/FloatingFieldModel.java +++ b/src/main/java/net/openhft/chronicle/values/FloatingFieldModel.java @@ -302,7 +302,7 @@ FloatingFieldModel.this.type, oldName(), @Override void generateEquals(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { methodBuilder.addCode( - format("if ($N(%s) != $N(other.$N())) return false;\n", + format("if ($N(%s) != $N(other.$N())) return false;%n", wrap(valueBuilder, methodBuilder, "$N")), toBits(), field, toBits(), getOrGetVolatile().getName()); } @@ -312,7 +312,7 @@ void generateArrayElementEquals( ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) { methodBuilder.addCode( - format("if ($N(%s) != $N(other.$N(index))) return false;\n", + format("if ($N(%s) != $N(other.$N(index))) return false;%n", wrap(valueBuilder, methodBuilder, "$N[index]")), toBits(), field, toBits(), arrayFieldModel.getOrGetVolatile().getName()); } diff --git a/src/main/java/net/openhft/chronicle/values/Generators.java b/src/main/java/net/openhft/chronicle/values/Generators.java index 10dde49d6..4555f1d14 100644 --- a/src/main/java/net/openhft/chronicle/values/Generators.java +++ b/src/main/java/net/openhft/chronicle/values/Generators.java @@ -140,8 +140,8 @@ private static void generateValueCommons(ValueBuilder valueBuilder, ImplType imp .addMethod(writeMarshallableMethod(valueBuilder, implType)) .addMethod(readMarshallableMethod(valueBuilder, implType)) .addMethod(equalsMethod(valueBuilder, implType)) - .addMethod(hashCodeMethod(valueBuilder, implType)); -// .addMethod(toStringMethod(valueBuilder, implType)); + .addMethod(hashCodeMethod(valueBuilder, implType)) + .addMethod(toStringMethod(valueBuilder, implType)); } /** diff --git a/src/main/java/net/openhft/chronicle/values/MyJavaFileManager.java b/src/main/java/net/openhft/chronicle/values/MyJavaFileManager.java index 20bd92f6c..9c0f5e612 100644 --- a/src/main/java/net/openhft/chronicle/values/MyJavaFileManager.java +++ b/src/main/java/net/openhft/chronicle/values/MyJavaFileManager.java @@ -33,14 +33,14 @@ */ public class MyJavaFileManager extends net.openhft.compiler.MyJavaFileManager { - /** + /* * Cache of classes required by generated code, keyed by package name. * Populated once in the static block below and shared across all * instances. */ private static final Map> dependencyFileObjects = new HashMap<>(); - /** + /* * Preloads {@code dependencyFileObjects} with Chronicle classes used by * generated code. */ @@ -118,7 +118,7 @@ private static JavaFileObject classFileObject(Class c) { URI uri = c.getResource(className.substring(lastDotIndex + 1) + ".class").toURI(); return new SimpleURIClassObject(uri, c); } catch (URISyntaxException e) { - throw new RuntimeException(e); + throw new IllegalStateException("Unable to resolve class URI for " + c.getName(), e); } } diff --git a/src/main/java/net/openhft/chronicle/values/PrimitiveBackedHeapMemberGenerator.java b/src/main/java/net/openhft/chronicle/values/PrimitiveBackedHeapMemberGenerator.java index c0e4a8235..1ef9c44a6 100644 --- a/src/main/java/net/openhft/chronicle/values/PrimitiveBackedHeapMemberGenerator.java +++ b/src/main/java/net/openhft/chronicle/values/PrimitiveBackedHeapMemberGenerator.java @@ -5,6 +5,8 @@ import com.squareup.javapoet.MethodSpec; +import java.util.Locale; + import static net.openhft.chronicle.values.Primitives.boxed; import static net.openhft.chronicle.values.Utils.capitalize; @@ -43,7 +45,7 @@ class PrimitiveBackedHeapMemberGenerator extends HeapMemberGenerator { fieldType = determineFieldType(); assert fieldType.isPrimitive(); capType = capitalize(fieldType.getName()); - upperType = fieldType.getName().toUpperCase(); + upperType = fieldType.getName().toUpperCase(Locale.ROOT); } PrimitiveBackedHeapMemberGenerator(FieldModel fieldModel, Class fieldType) { @@ -51,7 +53,7 @@ class PrimitiveBackedHeapMemberGenerator extends HeapMemberGenerator { this.fieldType = fieldType; assert fieldType.isPrimitive(); capType = capitalize(fieldType.getName()); - upperType = fieldType.getName().toUpperCase(); + upperType = fieldType.getName().toUpperCase(Locale.ROOT); } @Override diff --git a/src/main/java/net/openhft/chronicle/values/SimpleURIClassObject.java b/src/main/java/net/openhft/chronicle/values/SimpleURIClassObject.java index 3523330a4..32ebcd960 100644 --- a/src/main/java/net/openhft/chronicle/values/SimpleURIClassObject.java +++ b/src/main/java/net/openhft/chronicle/values/SimpleURIClassObject.java @@ -9,6 +9,7 @@ import java.io.*; import java.net.URI; import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; /** * Lightweight {@link JavaFileObject} backed by a {@link URI}. *

@@ -85,7 +86,7 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept @Override public Writer openWriter() throws IOException { - return new OutputStreamWriter(this.openOutputStream()); + return new OutputStreamWriter(this.openOutputStream(), StandardCharsets.UTF_8); } @Override diff --git a/src/main/java/net/openhft/chronicle/values/Utils.java b/src/main/java/net/openhft/chronicle/values/Utils.java index bb8003463..32bde9aa0 100644 --- a/src/main/java/net/openhft/chronicle/values/Utils.java +++ b/src/main/java/net/openhft/chronicle/values/Utils.java @@ -3,6 +3,8 @@ */ package net.openhft.chronicle.values; +import java.util.Locale; + /** * Miscellaneous helper methods used during code generation. * diff --git a/src/main/java/net/openhft/chronicle/values/ValueModel.java b/src/main/java/net/openhft/chronicle/values/ValueModel.java index c761abbed..927d89be9 100644 --- a/src/main/java/net/openhft/chronicle/values/ValueModel.java +++ b/src/main/java/net/openhft/chronicle/values/ValueModel.java @@ -64,7 +64,8 @@ protected Object computeValue(Class valueType) { this.valueType = valueType; orderedFields = new ArrayList<>(); sizeInBytes = arrangeFields(fields); - CACHED_COMPILER.fileManagerOverride = (fm) -> new MyJavaFileManager(valueType, fm); + CACHED_COMPILER.fileManagerOverride = + (fm) -> new MyJavaFileManager(valueType, fm); CACHED_COMPILER.updateFileManagerForClassLoader(valueType.getClassLoader(), fm -> { if (fm instanceof MyJavaFileManager) { ((MyJavaFileManager) fm).addClassToFileObjects(valueType); From 73c342a1c0f6e3f7822a79d51b6dec57534b6b4c Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Thu, 20 Nov 2025 20:31:31 +0000 Subject: [PATCH 3/4] Remove commented-out code and simplify test method signatures in PointerTest and VolatileTest --- .../openhft/chronicle/values/VolatileTest.java | 16 +--------------- .../chronicle/values/pointer/PointerTest.java | 2 -- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/test/java/net/openhft/chronicle/values/VolatileTest.java b/src/test/java/net/openhft/chronicle/values/VolatileTest.java index 6640fee5b..4994321db 100644 --- a/src/test/java/net/openhft/chronicle/values/VolatileTest.java +++ b/src/test/java/net/openhft/chronicle/values/VolatileTest.java @@ -20,22 +20,8 @@ public class VolatileTest extends ValuesTestCommon { @SuppressWarnings({"rawtypes", "unchecked"}) @Test - public void testGenerateJavaCode() throws ClassNotFoundException, IllegalAccessException, InstantiationException { + public void testGenerateJavaCode() { - /* try{ - BadInterface1 jbi = dvg.heapInstance(BadInterface1.class); - assertFalse("Should have thrown an IllegalArgumentException", true); - }catch(AssertionError e){ - assertTrue("Throws an IllegalArgumentException", true); - } - - try{ - BadInterface2 jbi = dvg.heapInstance(BadInterface2.class); - assertFalse("Should have thrown an IllegalArgumentException", true); - }catch(AssertionError e){ - assertTrue("Throws an IllegalArgumentException", true); - } -*/ //Test the heap interface try { GoodInterface jbi = newHeapInstance(GoodInterface.class); diff --git a/src/test/java/net/openhft/chronicle/values/pointer/PointerTest.java b/src/test/java/net/openhft/chronicle/values/pointer/PointerTest.java index 796d574d3..f8ab4bade 100644 --- a/src/test/java/net/openhft/chronicle/values/pointer/PointerTest.java +++ b/src/test/java/net/openhft/chronicle/values/pointer/PointerTest.java @@ -31,8 +31,6 @@ private static PointedInterface getPointed() { @Test public void testPointer() { -// System.setProperty("chronicle.values.dumpCode", "true"); - PointedInterface pointedFoo = getPointed(); assertTrue(pointedFoo.offset() < 10_000); pointedFoo.setString("foo"); From f8ae40b6bda352e201cf991fa10c5da62c378773 Mon Sep 17 00:00:00 2001 From: Peter Lawrey Date: Thu, 20 Nov 2025 23:30:22 +0000 Subject: [PATCH 4/4] Refactor code for improved readability and consistency in various classes --- README.adoc | 6 +- .../openhft/chronicle/values/BytecodeGen.java | 2 +- .../chronicle/values/EnumFieldModel.java | 1 - .../chronicle/values/HeapByteable.java | 3 +- .../values/SimpleURIClassObject.java | 4 +- .../net/openhft/chronicle/values/Utils.java | 2 - .../openhft/chronicle/values/ValueModel.java | 9 +-- .../chronicle/values/ComplexValueTest.java | 4 +- .../chronicle/values/CoreValuesTest.java | 20 +++--- .../chronicle/values/ValueGeneratorTest.java | 29 +------- .../values/ValueInterfaceWithEnumTest.java | 8 +-- .../chronicle/values/VolatileTest.java | 67 ++++++++----------- 12 files changed, 60 insertions(+), 95 deletions(-) diff --git a/README.adoc b/README.adoc index 02e02e47f..887ae0784 100644 --- a/README.adoc +++ b/README.adoc @@ -246,7 +246,7 @@ See the Javadocs of `@Align` and `@Array` for details. == Using Chronicle Values -[source,java] +[source,java,opts=novalidate] ---- // Flyweight Point offHeapPoint = Values.newNativeReference(Point.class); @@ -272,9 +272,9 @@ This provides a simple way to shuttle data between the two representations. You may extend these in the interface to avoid casting: -[source,java] +[source,java,opts=novalidate] ---- -interface Point extends Byteable, BytesMarshallable, Copyable { ... } +interface Point extends Byteable, BytesMarshallable, Copyable { /* code */ } Point offHeapPoint = Values.newNativeReference(Point.class); // no cast required diff --git a/src/main/java/net/openhft/chronicle/values/BytecodeGen.java b/src/main/java/net/openhft/chronicle/values/BytecodeGen.java index ecbc7712c..0b5640133 100644 --- a/src/main/java/net/openhft/chronicle/values/BytecodeGen.java +++ b/src/main/java/net/openhft/chronicle/values/BytecodeGen.java @@ -99,7 +99,7 @@ private static > T getSystemOption(final String name, T defaul String value = null; try { value = doPrivileged(() -> Jvm.getProperty(name)); - return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : + return (value != null && !value.isEmpty()) ? Enum.valueOf(enumType, value) : defaultValue; } catch (SecurityException e) { return secureValue; diff --git a/src/main/java/net/openhft/chronicle/values/EnumFieldModel.java b/src/main/java/net/openhft/chronicle/values/EnumFieldModel.java index 9f1060034..ad3bb0f36 100644 --- a/src/main/java/net/openhft/chronicle/values/EnumFieldModel.java +++ b/src/main/java/net/openhft/chronicle/values/EnumFieldModel.java @@ -6,7 +6,6 @@ import com.squareup.javapoet.ArrayTypeName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; -import net.openhft.chronicle.core.Jvm; import java.lang.reflect.Method; diff --git a/src/main/java/net/openhft/chronicle/values/HeapByteable.java b/src/main/java/net/openhft/chronicle/values/HeapByteable.java index 5f6ab09e9..cd9034ec7 100644 --- a/src/main/java/net/openhft/chronicle/values/HeapByteable.java +++ b/src/main/java/net/openhft/chronicle/values/HeapByteable.java @@ -5,6 +5,7 @@ import net.openhft.chronicle.bytes.Byteable; import net.openhft.chronicle.bytes.BytesStore; +import org.jetbrains.annotations.NotNull; /** * Default {@link Byteable} behaviour for heap-backed values. Each method @@ -21,7 +22,7 @@ public interface HeapByteable extends Byteable { * @throws UnsupportedOperationException always */ @Override - default void bytesStore(BytesStore bytesStore, long l, long l1) { + default void bytesStore(@NotNull BytesStore bytesStore, long l, long l1) { throw new UnsupportedOperationException(getClass() + " doesn't support Byteable interface"); } diff --git a/src/main/java/net/openhft/chronicle/values/SimpleURIClassObject.java b/src/main/java/net/openhft/chronicle/values/SimpleURIClassObject.java index 32ebcd960..e7bf6da07 100644 --- a/src/main/java/net/openhft/chronicle/values/SimpleURIClassObject.java +++ b/src/main/java/net/openhft/chronicle/values/SimpleURIClassObject.java @@ -62,7 +62,7 @@ public InputStream openInputStream() throws IOException { } @Override - public OutputStream openOutputStream() throws IOException { + public OutputStream openOutputStream() { throw new UnsupportedOperationException(); } @@ -80,7 +80,7 @@ public Reader openReader(boolean ignoreEncodingErrors) throws IOException { } @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + public CharSequence getCharContent(boolean ignoreEncodingErrors) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/net/openhft/chronicle/values/Utils.java b/src/main/java/net/openhft/chronicle/values/Utils.java index 32bde9aa0..bb8003463 100644 --- a/src/main/java/net/openhft/chronicle/values/Utils.java +++ b/src/main/java/net/openhft/chronicle/values/Utils.java @@ -3,8 +3,6 @@ */ package net.openhft.chronicle.values; -import java.util.Locale; - /** * Miscellaneous helper methods used during code generation. * diff --git a/src/main/java/net/openhft/chronicle/values/ValueModel.java b/src/main/java/net/openhft/chronicle/values/ValueModel.java index 927d89be9..c355deff2 100644 --- a/src/main/java/net/openhft/chronicle/values/ValueModel.java +++ b/src/main/java/net/openhft/chronicle/values/ValueModel.java @@ -4,6 +4,7 @@ package net.openhft.chronicle.values; import net.openhft.chronicle.core.Jvm; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -45,7 +46,7 @@ public class ValueModel { public static final String $$HEAP = "$$Heap"; private static final ClassValue classValueModel = new ClassValue() { @Override - protected Object computeValue(Class valueType) { + protected Object computeValue(@NotNull Class valueType) { try { return CodeTemplate.createValueModel(valueType); } catch (Exception e) { @@ -349,7 +350,7 @@ private Class createClass( } private static class FieldData { - int bitOffset; + final int bitOffset; int bitExtent; private FieldData(int bitOffset, int bitExtent) { @@ -359,8 +360,8 @@ private FieldData(int bitOffset, int bitExtent) { } private static class BitRange { - int from; - int to; + final int from; + final int to; BitRange(int from, int to) { this.from = from; diff --git a/src/test/java/net/openhft/chronicle/values/ComplexValueTest.java b/src/test/java/net/openhft/chronicle/values/ComplexValueTest.java index 3b93d1baf..eedd5dae9 100644 --- a/src/test/java/net/openhft/chronicle/values/ComplexValueTest.java +++ b/src/test/java/net/openhft/chronicle/values/ComplexValueTest.java @@ -22,9 +22,9 @@ public void heapAndNativeBehaviourMatch() { ComplexValue heap = newHeapInstance(ComplexValue.class); ComplexValue nativeValue = newNativeReference(ComplexValue.class); BytesStore store = - BytesStore.nativeStoreWithFixedCapacity(((Byteable) nativeValue).maxSize()); + BytesStore.nativeStoreWithFixedCapacity(nativeValue.maxSize()); try { - ((Byteable) nativeValue).bytesStore(store, 0, ((Byteable) nativeValue).maxSize()); + nativeValue.bytesStore(store, 0, nativeValue.maxSize()); mutateComplexValue(heap); mutateComplexValue(nativeValue); diff --git a/src/test/java/net/openhft/chronicle/values/CoreValuesTest.java b/src/test/java/net/openhft/chronicle/values/CoreValuesTest.java index 722f1c7b1..61eb94c74 100644 --- a/src/test/java/net/openhft/chronicle/values/CoreValuesTest.java +++ b/src/test/java/net/openhft/chronicle/values/CoreValuesTest.java @@ -231,20 +231,20 @@ public void testNativeFloatValue() { * Assertions shared by heap and native {@link FloatValue} tests. */ private void testFloatValue(FloatValue v) { - assertTrue(0.0f == v.getValue()); + assertEquals(0.0f, v.getValue(), 0.0); v.setValue(1.0f); - assertTrue(1.0f == v.getValue()); + assertEquals(1.0f, v.getValue(), 0.0); v.addValue(1.0f); - assertTrue(1.0f + 1.0f == v.getValue()); + assertEquals(1.0f + 1.0f, v.getValue(), 0.0); float v2 = v.getValue(); v.addAtomicValue(-1.0f); - assertTrue(v2 + (-1.0f) == v.getValue()); + assertEquals(v2 + (-1.0f), v.getValue(), 0.0); v.setOrderedValue(3.0f); - assertTrue(3.0f == v.getValue()); + assertEquals(3.0f, v.getValue(), 0.0); } /** @@ -291,20 +291,20 @@ public void testDoubleValueEquals() { * Common set of assertions for {@link DoubleValue} instances. */ private void testDoubleValue(DoubleValue v) { - assertTrue(0.0 == v.getValue()); + assertEquals(0.0, v.getValue(), 0.0); v.setValue(1.0); - assertTrue(1.0 == v.getValue()); + assertEquals(1.0, v.getValue(), 0.0); v.addValue(1.0); - assertTrue(1.0 + 1.0 == v.getValue()); + assertEquals(1.0 + 1.0, v.getValue(), 0.0); double v2 = v.getValue(); v.addAtomicValue(-1.0); - assertTrue(v2 + (-1.0) == v.getValue()); + assertEquals(v2 + (-1.0), v.getValue(), 0.0); v.setOrderedValue(3.0); - assertTrue(3.0 == v.getValue()); + assertEquals(3.0, v.getValue(), 0.0); } /** diff --git a/src/test/java/net/openhft/chronicle/values/ValueGeneratorTest.java b/src/test/java/net/openhft/chronicle/values/ValueGeneratorTest.java index 0e753821a..6427f5a8d 100644 --- a/src/test/java/net/openhft/chronicle/values/ValueGeneratorTest.java +++ b/src/test/java/net/openhft/chronicle/values/ValueGeneratorTest.java @@ -25,28 +25,6 @@ */ @SuppressWarnings({"rawtypes", "unchecked"}) public class ValueGeneratorTest extends ValuesTestCommon { - @Test - public void testGenerateJavaCode() { -// JavaBeanInterface jbi = Values.newHeapInstance(JavaBeanInterface.class); -// jbi.setByte((byte) 1); -// jbi.setChar('2'); -// jbi.setShort((short) 3); -// jbi.setInt(4); -// jbi.setFloat(5); -// jbi.setLong(6); -// jbi.setDouble(7); -// jbi.setFlag(true); -// -// assertEquals(1, jbi.getByte()); -// assertEquals('2', jbi.getChar()); -// assertEquals(3, jbi.getShort()); -// assertEquals(4, jbi.getInt()); -// assertEquals(5.0, jbi.getFloat(), 0); -// assertEquals(6, jbi.getLong()); -// assertEquals(7.0, jbi.getDouble(), 0.0); -// assertTrue(jbi.getFlag()); - } - @Test public void testGenerateJavaCode2() { MinimalInterface mi = newHeapInstance(MinimalInterface.class); @@ -96,7 +74,7 @@ public void testGenerateNativeWithGetUsing() throws ClassNotFoundException, Ille Class aClass = CACHED_COMPILER.loadFromJava( BytecodeGen.getClassLoader(JavaBeanInterfaceGetUsing.class), JavaBeanInterfaceGetUsing.class.getName() + "$$Native", actual); - JavaBeanInterfaceGetUsing jbi = (JavaBeanInterfaceGetUsing) aClass.asSubclass(JavaBeanInterfaceGetUsing.class).getDeclaredConstructor().newInstance(); + JavaBeanInterfaceGetUsing jbi = aClass.asSubclass(JavaBeanInterfaceGetUsing.class).getDeclaredConstructor().newInstance(); BytesStore bytes = BytesStore.wrap(ByteBuffer.allocate(64)); ((Byteable) jbi).bytesStore(bytes, 0L, ((Byteable) jbi).maxSize()); @@ -192,7 +170,7 @@ private T loadNativeTypeAndCreateValue(Class type) throws InstantiationEx Class aClass = Values.nativeClassFor(type); T jbi; try { - jbi = (T) aClass.asSubclass(type).getConstructor().newInstance(); + jbi = aClass.asSubclass(type).getConstructor().newInstance(); } catch (NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } @@ -206,8 +184,7 @@ private T loadHeapTypeAndCreateValue(Class type) throws InstantiationExce ValueModel.simpleName(type) + "$$Heap"); System.out.println(actual); Class aClass = Values.heapClassFor(type); - T jbi = (T) aClass.asSubclass(type).getDeclaredConstructor().newInstance(); - return jbi; + return (T) aClass.asSubclass(type).getDeclaredConstructor().newInstance(); } @Test diff --git a/src/test/java/net/openhft/chronicle/values/ValueInterfaceWithEnumTest.java b/src/test/java/net/openhft/chronicle/values/ValueInterfaceWithEnumTest.java index cfe84648a..096d91bbc 100644 --- a/src/test/java/net/openhft/chronicle/values/ValueInterfaceWithEnumTest.java +++ b/src/test/java/net/openhft/chronicle/values/ValueInterfaceWithEnumTest.java @@ -8,7 +8,7 @@ import org.junit.Test; import static net.openhft.chronicle.values.ValueInterfaceWithEnumTest.SimpleValueInterface.SVIEnum.SIX; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; /** * @author ges @@ -35,7 +35,7 @@ public void testValueInterface() { heapValue.copyFrom(nativeValue); assertEquals(1, heapValue.getId()); - assertEquals(true, heapValue.getTruth()); + assertTrue(heapValue.getTruth()); assertEquals(SIX, heapValue.getSVIEnum()); heapValue.setId(2); @@ -45,8 +45,8 @@ public void testValueInterface() { nativeValue.copyFrom(heapValue); assertEquals(2, nativeValue.getId()); - assertEquals(false, nativeValue.getTruth()); - assertEquals(null, nativeValue.getSVIEnum()); + assertFalse(nativeValue.getTruth()); + assertNull(nativeValue.getSVIEnum()); } public interface SimpleValueInterface extends Copyable { diff --git a/src/test/java/net/openhft/chronicle/values/VolatileTest.java b/src/test/java/net/openhft/chronicle/values/VolatileTest.java index 4994321db..996376159 100644 --- a/src/test/java/net/openhft/chronicle/values/VolatileTest.java +++ b/src/test/java/net/openhft/chronicle/values/VolatileTest.java @@ -12,7 +12,6 @@ import static net.openhft.chronicle.values.Values.newHeapInstance; import static net.openhft.chronicle.values.Values.newNativeReference; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; /* * Created by daniel on 11/06/2014. @@ -23,46 +22,36 @@ public class VolatileTest extends ValuesTestCommon { public void testGenerateJavaCode() { //Test the heap interface - try { - GoodInterface jbi = newHeapInstance(GoodInterface.class); - - jbi.setOrderedY(5); - assertEquals(5, jbi.getVolatileY()); - jbi.setOrderedIntAt(0, 0); - jbi.setOrderedIntAt(1, 1); - jbi.setOrderedIntAt(2, 2); - jbi.setOrderedIntAt(3, 3); - - assertEquals(0, jbi.getVolatileIntAt(0)); - assertEquals(1, jbi.getVolatileIntAt(1)); - assertEquals(2, jbi.getVolatileIntAt(2)); - assertEquals(3, jbi.getVolatileIntAt(3)); - } catch (AssertionError e) { - e.printStackTrace(); - assertFalse("Throws an IllegalArgumentException", true); - } + GoodInterface jbi = newHeapInstance(GoodInterface.class); + + jbi.setOrderedY(5); + assertEquals(5, jbi.getVolatileY()); + jbi.setOrderedIntAt(0, 0); + jbi.setOrderedIntAt(1, 1); + jbi.setOrderedIntAt(2, 2); + jbi.setOrderedIntAt(3, 3); + + assertEquals(0, jbi.getVolatileIntAt(0)); + assertEquals(1, jbi.getVolatileIntAt(1)); + assertEquals(2, jbi.getVolatileIntAt(2)); + assertEquals(3, jbi.getVolatileIntAt(3)); //Test the native interface - try { - GoodInterface jbi = newNativeReference(GoodInterface.class); - BytesStore bytes = BytesStore.wrap(ByteBuffer.allocate(64)); - ((Byteable) jbi).bytesStore(bytes, 0L, ((Byteable) jbi).maxSize()); - - jbi.setOrderedY(5); - assertEquals(5, jbi.getVolatileY()); - jbi.setOrderedIntAt(0, 0); - jbi.setOrderedIntAt(1, 1); - jbi.setOrderedIntAt(2, 2); - jbi.setOrderedIntAt(3, 3); - - assertEquals(0, jbi.getVolatileIntAt(0)); - assertEquals(1, jbi.getVolatileIntAt(1)); - assertEquals(2, jbi.getVolatileIntAt(2)); - assertEquals(3, jbi.getVolatileIntAt(3)); - } catch (AssertionError e) { - e.printStackTrace(); - assertFalse("Throws an IllegalArgumentException", true); - } + GoodInterface jbi2 = newNativeReference(GoodInterface.class); + BytesStore bytes = BytesStore.wrap(ByteBuffer.allocate(64)); + ((Byteable) jbi2).bytesStore(bytes, 0L, ((Byteable) jbi2).maxSize()); + + jbi2.setOrderedY(5); + assertEquals(5, jbi2.getVolatileY()); + jbi2.setOrderedIntAt(0, 0); + jbi2.setOrderedIntAt(1, 1); + jbi2.setOrderedIntAt(2, 2); + jbi2.setOrderedIntAt(3, 3); + + assertEquals(0, jbi2.getVolatileIntAt(0)); + assertEquals(1, jbi2.getVolatileIntAt(1)); + assertEquals(2, jbi2.getVolatileIntAt(2)); + assertEquals(3, jbi2.getVolatileIntAt(3)); } public interface BadInterface1 {