Advanced topics
This topic discusses miscellaneous information on topics that are not mandatory for using Copper in a project, but can be useful in certain situations.
Cross compatibility with non-Copper definitions
Defining measures using both Copper 2 and the classic ways (like the builders or using the measure definitions) should work seamlessly.
Copper is also able to handle non-Copper analysis levels and hierarchies, under the condition that the property in com.quartetfs.biz.pivot.cube.hierarchy.IAnalysisHierarchy#LEVEL_TYPES_PROPERTY
be specified. This is because Copper is not aware of the level names or types for the analysis hierarchies defined outside of Copper (like Java-based analysis hierarchies). in such cases you need to provide that information in the hierarchy properties.
Example:
...
.withHierarchy(HIERARCHY_NAME).slicing()
.withPluginKey(ANALYSIS_HIERARCHY_PLUGIN_KEY)
.withProperty(IAnalysisHierarchy.LEVEL_TYPES_PROPERTY, LEVEL_NAME + ":" + Types.TYPE_LONG)
...
Using the Copper testing framework
One of the objectives of Copper is to be easily testable. This is why it bundles a lot of utilities and APIs to help test your calculations. The main one is the CubeTester.
Since the testing framework is only needed for tests and require test dependencies, it is not available in the activepivot-copper2-impl maven module. You must import the activepivot-copper2-test module before using it:
<dependency>
<groupId>com.activeviam.activepivot</groupId>
<artifactId>activepivot-copper2-test</artifactId>
<scope>test</scope>
</dependency>
Defining a test environment
We use JUnit rules to define a builder field that is reset at each test. This rule allows us to automate the creation and deletion of multiple ActivePivot components in order to make the tests more concise and entirely focused on the tested features.
@RegisterExtension
public CubeTesterBuilderExtension builder = new CubeTesterBuilderExtension(() -> createTester());
In the definition of the builder, we provide a static function that returns a CubeTesterBuilder, in which you define the specifics of your datastore, datastore selection, and cube descriptions:
public static CubeTesterBuilder createTester() {
final IDatastoreSchemaDescription datastoreDescription =
DatastoreDescriptionConfig.schemaDescription(null);
final ISelectionDescription selectionDescription =
EquityDerivativesManagerConfig.createSandboxSchemaSelectionDescription(
datastoreDescription);
final IActivePivotInstanceDescription cubeDescription =
StartBuilding.cube()
.withName("C")
.withDimensions(EquityDerivativesCubeDimensionsConfig::dimensions)
.build();
return new CubeTesterBuilder(
datastoreDescription, selectionDescription, createTestData(), cubeDescription);
}
In the tests, you can call the builder.build(consumer)
method, where consumer
is a
Consumer<ICopperContext>
(e.g. a Java lambda containing the tested Copper code),
and obtain the built testing cube:
final CubeTester tester = builder.build(EquityDerivativesCubeMeasuresConfig::pnlMonitoring);
pnlMonitoring()
is a static function, such as:
public static void pnlMonitoring(ICopperContext context) {
Copper.combine(Copper.sum(RISK__PNL), Copper.constant(0d).withName(PNL_LIMIT))
.mapToDouble(a -> a.readDouble(0) >= a.readDouble(1) ? 1 : -1)
.as(PNL_STATUS)
.publish(context);
}
And then you can use the subsequent tester to perform inquiries on data returned by queries like:
tester.query().forMeasures(PNL_LIMIT).run().getTester().hasOnlyOneCell().containing(0D);
or some meta-inquiries on the state of the cube after being enriched by Copper:
tester.description().hasOnlyOneMeasure().hasOnlyOnePreAggregatedMeasure().withName("myMeasure");
Using the testers
As mentioned before, different methods of the obtained {CubeTester} can be used for various needs:
query()
starts the builder of aCompiledQuery
that can be customized extensively and executed with therun()
method. The obtained results can be tested on every location.description()
returns a tester allowing you to test the status of the cube, such as the presence of measures, the hierarchies count, and so on.mdxQuery(mdx, contextValues...)
runs an MDX query and returns the obtained cellset.manager()
returns the manager attached to the tested cube.pivot()
returns the tested cube.