ctf: Add a constant to get the currently selected field for variant types
authorGeneviève Bastien <gbastien+lttng@versatic.net>
Tue, 12 Jul 2016 19:47:28 +0000 (15:47 -0400)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Fri, 22 Jul 2016 18:20:04 +0000 (14:20 -0400)
Variants typically have only 1 struct fields per choice and its name is the
tag. Many possible choices may have the same fields in the struct so it should
be possible to use one lookup to check in every possible choice. This patch
adds a constant to use to get the currently selected field no matter its name.

Use case for this: network packets in LTTng traces have a network_header
variant field that contains a transport_header variant field. But the various
network_header choices have the same transport_header field. We want to be
able to query the { network_header, ANY, transport_header } field and not, as
previously { network_header, ipv4, transport_header } or { network_header,
ipv6, transport_header }.

Change-Id: I74b1e0494a43eae9a9f91dca575d5419f3907168
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/77172
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Reviewed-by: Hudson CI
ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/ctf/core/tests/event/CtfTmfEventFieldVariantTest.java [new file with mode: 0644]
ctf/org.eclipse.tracecompass.tmf.ctf.core/src/org/eclipse/tracecompass/tmf/ctf/core/event/CtfTmfEventField.java

diff --git a/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/ctf/core/tests/event/CtfTmfEventFieldVariantTest.java b/ctf/org.eclipse.tracecompass.tmf.ctf.core.tests/src/org/eclipse/tracecompass/tmf/ctf/core/tests/event/CtfTmfEventFieldVariantTest.java
new file mode 100644 (file)
index 0000000..04c6c9b
--- /dev/null
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.ctf.core.tests.event;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.ctf.core.CTFException;
+import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer;
+import org.eclipse.tracecompass.ctf.core.event.types.Encoding;
+import org.eclipse.tracecompass.ctf.core.event.types.EnumDeclaration;
+import org.eclipse.tracecompass.ctf.core.event.types.IDefinition;
+import org.eclipse.tracecompass.ctf.core.event.types.IntegerDeclaration;
+import org.eclipse.tracecompass.ctf.core.event.types.StringDeclaration;
+import org.eclipse.tracecompass.ctf.core.event.types.StructDeclaration;
+import org.eclipse.tracecompass.ctf.core.event.types.StructDefinition;
+import org.eclipse.tracecompass.ctf.core.event.types.VariantDeclaration;
+import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEventField;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Add some unit tests specific for variant fields
+ *
+ * @author Geneviève Bastien
+ */
+public class CtfTmfEventFieldVariantTest {
+
+    /*
+     * Same string content, but not the same object, as the any variant selected
+     * string
+     */
+    private static final @NonNull String ENUM_ANY = new String(CtfTmfEventField.FIELD_VARIANT_SELECTED);
+    private static final @NonNull String ENUM_NAME2 = "choice2";
+    private static final @NonNull String ENUM_NAME3 = "choice3";
+    private static final int ENUM_VAL1 = 0;
+    private static final int ENUM_VAL2 = 1;
+    private static final int ENUM_VAL3 = 2;
+    private static final @NonNull String ENUM = "enum";
+
+    private static final @NonNull String FIELD1 = "Fiedl1";
+    private static final @NonNull String FIELD2 = "Field2";
+    private static final @NonNull String FIELD3 = "Field3";
+
+    private static final @NonNull String VARIANT = "variant";
+    private static final @NonNull String ROOT = "root";
+
+    private StructDeclaration fDeclaration;
+
+    /**
+     * Create the declaration for the variant field
+     */
+    @Before
+    public void createDeclaration() {
+        /**
+         * Create a variant field based on the following metadata
+         *
+         * <pre>
+         * enum : integer { size = 8; } {
+         *     "_Any" = 0,
+         *     "_var2" = 1,
+         *     "_var3" = 2,
+         * } _tag;
+         * variant <_tag> {
+         *     struct {
+         *        integer { size = 8; } _field1;
+         *        integer { size = 8; } _field2;
+         *     } _Any;
+         *     struct {
+         *         integer { size = 8; } _field1;
+         *         string _field2;
+         *         integer { size = 8; } _field3;
+         *     } _var2;
+         *     integer { size = 8; } _var3;
+         * } _mainVariant;
+         * </pre>
+         */
+        StructDeclaration sDec = new StructDeclaration(0);
+
+        /* Create enum field */
+        EnumDeclaration enumDec = new EnumDeclaration(IntegerDeclaration.INT_8_DECL);
+        enumDec.add(ENUM_VAL1, ENUM_VAL1, ENUM_ANY);
+        enumDec.add(ENUM_VAL2, ENUM_VAL2, ENUM_NAME2);
+        enumDec.add(ENUM_VAL3, ENUM_VAL3, ENUM_NAME3);
+        sDec.addField(ENUM, enumDec);
+
+        /* Create structure for first choice */
+        StructDeclaration choice1Dec = new StructDeclaration(0);
+        choice1Dec.addField(FIELD1, IntegerDeclaration.INT_8_DECL);
+        choice1Dec.addField(FIELD2, IntegerDeclaration.INT_8_DECL);
+
+        /* Create structure for second choice */
+        StructDeclaration choice2Dec = new StructDeclaration(0);
+        choice2Dec.addField(FIELD1, IntegerDeclaration.INT_8_DECL);
+        choice2Dec.addField(FIELD2, StringDeclaration.getStringDeclaration(Encoding.UTF8));
+        choice2Dec.addField(FIELD3, IntegerDeclaration.INT_8_DECL);
+
+        /* Create variant field */
+        VariantDeclaration varDec = new VariantDeclaration();
+        varDec.setTag(ENUM);
+        varDec.addField(ENUM_ANY, choice1Dec);
+        varDec.addField(ENUM_NAME2, choice2Dec);
+        varDec.addField(ENUM_NAME3, IntegerDeclaration.INT_8_DECL);
+
+        sDec.addField(VARIANT, varDec);
+        fDeclaration = sDec;
+    }
+
+    /**
+     * Test variant field getters
+     *
+     * @throws CTFException
+     *             exception occurring when reading a field
+     */
+    @Test
+    public void testVariantGetField() throws CTFException {
+        StructDeclaration decl = fDeclaration;
+        StructDefinition sDef = null;
+
+        /* Obtain a field with the first variant choice */
+        ByteBuffer bb = ByteBuffer.allocateDirect(1024);
+        bb.put((byte) ENUM_VAL1);
+        bb.put((byte) 3);
+        bb.put((byte) 4);
+
+        sDef = decl.createDefinition(null, ROOT, new BitBuffer(bb));
+        IDefinition definition = sDef.getDefinition(VARIANT);
+        CtfTmfEventField result = CtfTmfEventField.parseField(definition, VARIANT);
+
+        /** Assert it returns a value only for the correct enum */
+        assertNotNull(result.getField(ENUM_ANY));
+        assertNull(result.getField(ENUM_NAME2));
+        assertNull(result.getField(ENUM_NAME3));
+
+        /**
+         * Assert the subfields can be reached through the current enum and have
+         * the right value
+         */
+        assertNotNull(result.getField(ENUM_ANY, FIELD1));
+        assertEquals(3L, result.getField(ENUM_ANY, FIELD1).getValue());
+        assertNotNull(result.getField(ENUM_ANY, FIELD2));
+        assertEquals(4L, result.getField(ENUM_ANY, FIELD2).getValue());
+        assertNull(result.getField(ENUM_ANY, FIELD3));
+
+        /**
+         * Assert the subfields can be reached through the
+         * {@link CtfTmfEventField#FIELD_VARIANT_SELECTED} constant and have the
+         * right value
+         */
+        assertNotNull(result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD1));
+        assertEquals(3L, result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD1).getValue());
+        assertNotNull(result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD2));
+        assertEquals(4L, result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD2).getValue());
+        assertNull(result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD3));
+
+        /* Obtain a field with the second variant choice */
+        String testString = "My Test string";
+        bb = ByteBuffer.allocateDirect(1024);
+        bb.put((byte) ENUM_VAL2);
+        bb.put((byte) 3);
+        bb.put(testString.getBytes());
+        bb.put((byte) 0);
+        bb.put((byte) 4);
+
+        sDef = decl.createDefinition(null, ROOT, new BitBuffer(bb));
+        definition = sDef.getDefinition(VARIANT);
+        result = CtfTmfEventField.parseField(definition, VARIANT);
+
+        /** Assert it returns a value only for the correct enum */
+        assertNull(result.getField(ENUM_ANY));
+        assertNotNull(result.getField(ENUM_NAME2));
+        assertNull(result.getField(ENUM_NAME3));
+
+        /**
+         * Assert the subfields can be reached through the current enum and have
+         * the right value
+         */
+        assertNotNull(result.getField(ENUM_NAME2, FIELD1));
+        assertEquals(3L, result.getField(ENUM_NAME2, FIELD1).getValue());
+        assertNotNull(result.getField(ENUM_NAME2, FIELD2));
+        assertEquals(testString, result.getField(ENUM_NAME2, FIELD2).getValue());
+        assertNotNull(result.getField(ENUM_NAME2, FIELD3));
+        assertEquals(4L, result.getField(ENUM_NAME2, FIELD3).getValue());
+
+        /**
+         * Assert the subfields can be reached through the
+         * {@link CtfTmfEventField#FIELD_VARIANT_SELECTED} constant and have the
+         * right value
+         */
+        assertNotNull(result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD1));
+        assertEquals(3L, result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD1).getValue());
+        assertNotNull(result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD2));
+        assertEquals(testString, result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD2).getValue());
+        assertNotNull(result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD3));
+        assertEquals(4L, result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD3).getValue());
+
+        /**
+         * Assert that trying to reach a field of this choice with the same name
+         * as the current selection field will not return the field
+         */
+        assertNull(result.getField(ENUM_ANY, FIELD1));
+
+        /* Obtain a field with the third variant choice */
+        bb = ByteBuffer.allocateDirect(1024);
+        bb.put((byte) ENUM_VAL3);
+        bb.put((byte) 3);
+
+        sDef = decl.createDefinition(null, ROOT, new BitBuffer(bb));
+        definition = sDef.getDefinition(VARIANT);
+        result = CtfTmfEventField.parseField(definition, VARIANT);
+
+        /** Assert it returns a value only for the correct enum */
+        assertNull(result.getField(ENUM_ANY));
+        assertNull(result.getField(ENUM_NAME2));
+        assertNotNull(result.getField(ENUM_NAME3));
+
+        /**
+         * Assert the subfields return null
+         */
+        assertNull(result.getField(ENUM_NAME3, FIELD1));
+        assertNull(result.getField(ENUM_NAME3, FIELD2));
+        assertEquals(3L, result.getField(ENUM_NAME3).getValue());
+
+        /**
+         * Assert the subfields can be reached through the
+         * {@link CtfTmfEventField#FIELD_VARIANT_SELECTED} constant and have the
+         * right value
+         */
+        assertNotNull(result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED));
+        assertEquals(3L, result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED).getValue());
+        assertNull(result.getField(CtfTmfEventField.FIELD_VARIANT_SELECTED, FIELD1));
+    }
+
+}
index bef8078208cbca2c6c2602f7935019f821ac4de1..4f0f38c5df6bfb2fc8e9806ca55739d04d3b6c7f 100644 (file)
@@ -50,6 +50,15 @@ import org.eclipse.tracecompass.tmf.ctf.core.CtfEnumPair;
  */
 public abstract class CtfTmfEventField extends TmfEventField {
 
+    /**
+     * Value that can be used in the {@link #getField(String...)} for variants.
+     * Using this field value means that the selected field will be returned
+     * whatever the selected choice for the event
+     *
+     * @since 2.1
+     */
+    public static final @NonNull String FIELD_VARIANT_SELECTED = "Any"; //$NON-NLS-1$
+
     // ------------------------------------------------------------------------
     // Constructor
     // ------------------------------------------------------------------------
@@ -480,6 +489,18 @@ final class CTFVariantField extends CtfTmfEventField {
         return (CtfTmfEventField) super.getValue();
     }
 
+    @Override
+    public ITmfEventField getField(final String... path) {
+        /*
+         * We use the == to make sure that this constant was used, otherwise, it
+         * could conflict with a field with the same name
+         */
+        if (path.length == 1 && path[0] == FIELD_VARIANT_SELECTED) {
+            return getFields().stream().findFirst().orElse(null);
+        }
+        return super.getField(path);
+    }
+
 }
 
 /* Implement other possible fields types here... */
This page took 0.029866 seconds and 5 git commands to generate.