sink.text.pretty: add optional `print-enum-flags` parameter
authorGeneviève Bastien <gbastien@versatic.net>
Tue, 25 Feb 2020 20:33:49 +0000 (15:33 -0500)
committerFrancis Deslauriers <francis.deslauriers@efficios.com>
Thu, 6 Aug 2020 18:24:48 +0000 (14:24 -0400)
`print-enum-flags` parameter
============================
This new parameter tells the component to try to print enum values as
ORed bit flags if it applies.

When printing the value of an enum field that has no corresponding mapping,
the value is divided into its bit values and if each bit has a corresponding
mapping, consider that those mappings are ORed bit flags.

If any of the bits has no mapping, then the value is printed as <unknown>.

Example Babeltrace output of such a field (block_rq* kernel events):

  [13:15:49.024354958] (+0.000003868) wilbrod block_rq_complete: { cpu_id = 4 },
      { dev = 8388624, sector = 375490176, nr_sector = 360, error = 0,
       rwbs = ( "RWBS_FLAG_READ" | "RWBS_FLAG_RAHEAD" : container = 12 ) }

This parameter defaults to `false` as it may trigger unexpected behavior
if a trace contains a bit flag that is not described in the metadata as
is, but can be expressed as a binary OR of two existing values.
This can happen if an application changes the possible values of an
enumeration but forgets to update the metadata associated with
that recorded field. Such value must be printed as <unknown>.

Print all labels matching value
===============================
With this commit, a `sink.text.pretty` component will print all matching
labels inside curly brackets separated by commas. See
`print_enum_value_label_array()`.

So if an enum value matches two labels it will be printed as:
  enum_field = ( { "bit0", "range1to3" } : container = 1 )

Change-Id: I26a17307243d5612c70c0e5b04e6b9567b0720d3
Signed-off-by: Geneviève Bastien <gbastien@versatic.net>
Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/3045
Tested-by: jenkins <jenkins@lttng.org>
Reviewed-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/plugins/text/pretty/pretty.c
src/plugins/text/pretty/pretty.h
src/plugins/text/pretty/print.c
tests/plugins/sink.text.pretty/test_enum

index a3aa3f812f8669865851176a235e5bac6d27d46d..cad275145392b9391d74667d3abc1f6907e72ce0 100644 (file)
@@ -28,6 +28,7 @@ const char * const in_port_name = "in";
 static
 void destroy_pretty_data(struct pretty_component *pretty)
 {
+       uint64_t i;
        if (!pretty) {
                goto end;
        }
@@ -50,6 +51,13 @@ void destroy_pretty_data(struct pretty_component *pretty)
                        perror("close output file");
                }
        }
+
+       for (i = 0; i < ENUMERATION_MAX_BITFLAGS_COUNT; i++) {
+               if (pretty->enum_bit_labels[i]) {
+                       g_ptr_array_free(pretty->enum_bit_labels[i], true);
+               }
+       }
+
        g_free(pretty->options.output_path);
        g_free(pretty);
 
@@ -312,6 +320,7 @@ struct bt_param_validation_map_value_entry_descr pretty_params[] = {
        { "field-loglevel", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
        { "field-emf", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
        { "field-callsite", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
+       { "print-enum-flags", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
        BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
 };
 
@@ -381,6 +390,9 @@ bt_component_class_initialize_method_status apply_params(
        apply_one_bool_with_default("verbose", params,
                &pretty->options.verbose, false);
 
+       apply_one_bool_with_default("print-enum-flags", params,
+               &pretty->options.print_enum_flags, false);
+
        /* Names. */
        value = bt_value_map_borrow_entry_value_const(params, "name-default");
        if (value) {
@@ -584,6 +596,18 @@ bt_component_class_initialize_method_status pretty_init(
        }
 
        set_use_colors(pretty);
+
+       if (pretty->options.print_enum_flags) {
+               uint64_t i;
+               /*
+                * Allocate all label arrays during the initialization of the
+                * component and reuse the same set of arrays for all
+                * enumerations.
+                */
+               for (i = 0; i < ENUMERATION_MAX_BITFLAGS_COUNT; i++) {
+                       pretty->enum_bit_labels[i] = g_ptr_array_new();
+               }
+       }
        bt_self_component_set_data(self_comp, pretty);
 
        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
index 2df935402de31af7fd4fb8af7fc701965f7e835b..abf192966680f3c75266e3f9b3bb8d9db5623756 100644 (file)
 #include "common/macros.h"
 #include <babeltrace2/babeltrace.h>
 
+/*
+ * `bt_field_*_enumeration` are backed by 64 bits integers so the maximun
+ * number of bitflags in any enumeration is 64.
+ */
+#define ENUMERATION_MAX_BITFLAGS_COUNT (sizeof(uint64_t) * 8)
+
 enum pretty_default {
        PRETTY_DEFAULT_UNSET,
        PRETTY_DEFAULT_SHOW,
@@ -37,6 +43,7 @@ struct pretty_options {
        bool print_payload_field_names;
 
        bool print_delta_field;
+       bool print_enum_flags;
        bool print_loglevel_field;
        bool print_emf_field;
        bool print_callsite_field;
@@ -72,6 +79,18 @@ struct pretty_component {
 
        bool negative_timestamp_warning_done;
 
+       /*
+        * For each bit of the integer backing the enumeration we have a list
+        * (GPtrArray) of labels (char *) for that bit.
+        *
+        * Allocate all label arrays during the initialization of the component
+        * and reuse the same set of arrays for all enumerations. This prevents
+        * allocation and deallocation everytime the component encounters a
+        * enumeration field. Allocating and deallocating that often could
+        * severely impact performance.
+        */
+       GPtrArray *enum_bit_labels[ENUMERATION_MAX_BITFLAGS_COUNT];
+
        bt_logging_level log_level;
        bt_self_component *self_comp;
 };
index a4e7f6869be357eb769afd4bfc2ea8421626f59e..61d79cdb9459d74dacf12fd945a6822ea47fdbf2 100644 (file)
@@ -664,6 +664,301 @@ void print_escape_string(struct pretty_component *pretty, const char *str)
        bt_common_g_string_append_c(pretty->string, '"');
 }
 
+/*
+ * Print the unknown label.
+ */
+static
+void print_enum_value_label_unknown(struct pretty_component *pretty)
+{
+       if (pretty->use_colors) {
+               bt_common_g_string_append(pretty->string, color_unknown);
+       }
+
+       bt_common_g_string_append(pretty->string, "<unknown>");
+
+       if (pretty->use_colors) {
+               bt_common_g_string_append(pretty->string, color_rst);
+       }
+}
+
+/*
+ * Print labels for a value. If there are more than one label for the
+ * value, they will be printed in "{ }".
+ */
+static
+void print_enum_value_label_array(struct pretty_component *pretty,
+               uint64_t label_count,
+               bt_field_class_enumeration_mapping_label_array label_array)
+{
+       uint64_t i;
+
+       if (label_count > 1) {
+               bt_common_g_string_append(pretty->string, "{ ");
+       }
+
+       for (i = 0; i < label_count; i++) {
+               const char *mapping_name = label_array[i];
+
+               if (i != 0) {
+                       bt_common_g_string_append(pretty->string, ", ");
+               }
+               if (pretty->use_colors) {
+                       bt_common_g_string_append(pretty->string, color_enum_mapping_name);
+               }
+               print_escape_string(pretty, mapping_name);
+               if (pretty->use_colors) {
+                       bt_common_g_string_append(pretty->string, color_rst);
+               }
+       }
+
+       if (label_count > 1) {
+               bt_common_g_string_append(pretty->string, " }");
+       }
+}
+
+/*
+ * Print arrays of labels and counts are ORed bit flags.
+ */
+static
+void print_enum_value_bit_flag_label_arrays(struct pretty_component *pretty)
+{
+       uint64_t i;
+       bool first_label = true;
+
+       /* For each bit with a label count > 0, print the labels. */
+       for (i = 0; i < ENUMERATION_MAX_BITFLAGS_COUNT; i++) {
+               uint64_t label_count = pretty->enum_bit_labels[i]->len;
+
+               if (label_count > 0) {
+                       if (!first_label) {
+                               bt_common_g_string_append(pretty->string, " | ");
+                       }
+                       print_enum_value_label_array(pretty, label_count,
+                               (void *) pretty->enum_bit_labels[i]->pdata);
+                       first_label = false;
+               }
+       }
+}
+
+/*
+ * Get the labels mapping to an unsigned value.
+ *
+ * This function will set the count to the count of mapping labels that match
+ * the value. If count == 0, the caller has nothing to do, if count > 0,
+ * the label_array contains the labels to print and the caller is reponsible
+ * to call g_free on it once the values have been used.
+ */
+static
+void print_enum_unsigned_get_mapping_labels_for_value(const bt_field_class *fc,
+               uint64_t value, GPtrArray *labels)
+{
+       uint64_t mapping_count = bt_field_class_enumeration_get_mapping_count(fc);
+       uint64_t i;
+
+       for (i = 0; i < mapping_count; i++) {
+               uint64_t range_i;
+               const struct bt_field_class_enumeration_unsigned_mapping *mapping =
+                       bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(fc, i);
+               const bt_integer_range_set_unsigned *ranges =
+                       bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(mapping);
+               uint64_t range_count = bt_integer_range_set_get_range_count(
+                               bt_integer_range_set_unsigned_as_range_set_const(ranges));
+
+               for (range_i = 0; range_i < range_count; range_i++) {
+                       const bt_integer_range_unsigned *range =
+                               bt_integer_range_set_unsigned_borrow_range_by_index_const(ranges, range_i);
+                       uint64_t lower = bt_integer_range_unsigned_get_lower(range);
+                       uint64_t upper = bt_integer_range_unsigned_get_upper(range);
+
+                       /*
+                        * Flag is active if this range represents a single value
+                        * (lower == upper) and the lower is the same as the bit
+                        * value to test against.
+                        */
+                       if ((lower == upper) && (lower == value)) {
+                               g_ptr_array_add(labels, (void *) bt_field_class_enumeration_mapping_get_label(
+                                       bt_field_class_enumeration_unsigned_mapping_as_mapping_const(mapping)));
+                               break;
+                       }
+               }
+       }
+}
+
+/*
+ * Splits an unsigned enum value into its bit and for each bit set,
+ * try to find a corresponding label.
+ *
+ * If any bit set does not have a corresponding label, then it prints
+ * an unknown value, otherwise, it prints the labels, separated by '|'
+ */
+static
+void print_enum_unsigned_try_bit_flags(struct pretty_component *pretty,
+               const bt_field *field)
+{
+       uint64_t i;
+       uint64_t value = bt_field_integer_unsigned_get_value(field);
+       const bt_field_class *fc = bt_field_borrow_class_const(field);
+
+       /* Value is 0, if there was a label for it, we would know by now. */
+       if (value == 0) {
+               print_enum_value_label_unknown(pretty);
+               goto end;
+       }
+
+       for (i = 0; i < ENUMERATION_MAX_BITFLAGS_COUNT; i++) {
+               uint64_t bit_value = UINT64_C(1) << i;
+
+               if ((value & bit_value) != 0) {
+                       print_enum_unsigned_get_mapping_labels_for_value(
+                               fc, bit_value, pretty->enum_bit_labels[i]);
+
+                       if (pretty->enum_bit_labels[i]->len == 0) {
+                               /*
+                                * This bit has no matching label, so this
+                                * field is not a bit flag field, print
+                                * unknown and return.
+                                */
+                               print_enum_value_label_unknown(pretty);
+                               goto end;
+                       }
+               }
+       }
+
+       print_enum_value_bit_flag_label_arrays(pretty);
+
+end:
+       return;
+}
+
+/*
+ * Get the labels mapping to a signed value
+ *
+ * This function will set the count to the count of mapping labels that match
+ * the value. If count == 0, the caller has nothing to do, if count > 0,
+ * the label_array contains the labels to print and the caller is reponsible
+ * to call g_free on it once the values have been used.
+ */
+static
+void print_enum_signed_get_mapping_labels_for_value(const bt_field_class *fc,
+               int64_t value, GPtrArray *labels)
+{
+       uint64_t mapping_count = bt_field_class_enumeration_get_mapping_count(fc);
+       uint64_t i;
+
+       for (i = 0; i < mapping_count; i++) {
+               uint64_t range_i;
+               const struct bt_field_class_enumeration_signed_mapping *mapping =
+                       bt_field_class_enumeration_signed_borrow_mapping_by_index_const(fc, i);
+               const bt_integer_range_set_signed *ranges =
+                       bt_field_class_enumeration_signed_mapping_borrow_ranges_const(mapping);
+               uint64_t range_count = bt_integer_range_set_get_range_count(
+                               bt_integer_range_set_signed_as_range_set_const(ranges));
+
+               for (range_i = 0; range_i < range_count; range_i++) {
+                       const bt_integer_range_signed *range =
+                               bt_integer_range_set_signed_borrow_range_by_index_const(ranges, range_i);
+                       uint64_t lower = bt_integer_range_signed_get_lower(range);
+                       uint64_t upper = bt_integer_range_signed_get_upper(range);
+
+                       /*
+                        * Flag is active if this range represents a single value
+                        * (lower == upper) and the lower is the same as the bit
+                        * value to test against.
+                        */
+                       if ((lower == upper) && (lower == value)) {
+                               g_ptr_array_add(labels, (void*) bt_field_class_enumeration_mapping_get_label(
+                                       bt_field_class_enumeration_signed_mapping_as_mapping_const(mapping)));
+                               break;
+                       }
+               }
+       }
+}
+
+/*
+ * Splits a signed enum value into its bit and for each bit set,
+ * try to find a corresponding label.
+ *
+ * If any bit set does not have a corresponding label, then it prints
+ * an unknown value, otherwise, it prints the labels, separated by '|'.
+ */
+static
+void print_enum_signed_try_bit_flags(struct pretty_component *pretty,
+               const bt_field *field)
+{
+       uint64_t i;
+       int64_t value = bt_field_integer_signed_get_value(field);
+       const bt_field_class *fc = bt_field_borrow_class_const(field);
+
+       /*
+        * Negative value, not a bit flag enum
+        * For 0, if there was a value, we would know by now.
+        */
+       if (value <= 0) {
+               print_enum_value_label_unknown(pretty);
+               goto end;
+       }
+
+       for (i = 0; i < ENUMERATION_MAX_BITFLAGS_COUNT; i++) {
+               uint64_t bit_value = 1ULL << i;
+
+               if ((value & bit_value) != 0) {
+                       print_enum_signed_get_mapping_labels_for_value(
+                               fc, bit_value, pretty->enum_bit_labels[i]);
+
+                       if (pretty->enum_bit_labels[i]->len == 0) {
+                               /*
+                                * This bit has no matching label, so this
+                                * field is not a bit flag field, print
+                                * unknown and return.
+                                */
+                               print_enum_value_label_unknown(pretty);
+                               goto end;
+                       }
+               }
+       }
+
+       print_enum_value_bit_flag_label_arrays(pretty);
+
+end:
+       return;
+}
+
+/*
+ * Main function to try to print the value of the enum field as a
+ * bit flag. It calls the appropriate function depending on the
+ * field type's signedness and prints the label for each bit set,
+ * if it's a bit flag field.
+ */
+static
+void print_enum_try_bit_flags(struct pretty_component *pretty,
+               const bt_field *field)
+{
+       const bt_field_class *fc = bt_field_borrow_class_const(field);
+       uint64_t int_range = bt_field_class_integer_get_field_value_range(fc);
+       int i;
+
+       BT_ASSERT(int_range <= ENUMERATION_MAX_BITFLAGS_COUNT);
+
+       /*
+        * Remove all labels from the previous enumeration field.
+        */
+       for (i = 0; i < ENUMERATION_MAX_BITFLAGS_COUNT; i++) {
+               g_ptr_array_set_size(pretty->enum_bit_labels[i], 0);
+       }
+
+       /* Get the mapping labels for the bit value. */
+       switch (bt_field_class_get_type(fc)) {
+       case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
+               print_enum_unsigned_try_bit_flags(pretty, field);
+               break;
+       case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
+               print_enum_signed_try_bit_flags(pretty, field);
+               break;
+       default:
+               bt_common_abort();
+       }
+}
+
 static
 int print_enum(struct pretty_component *pretty,
                const bt_field *field)
@@ -671,7 +966,6 @@ int print_enum(struct pretty_component *pretty,
        int ret = 0;
        bt_field_class_enumeration_mapping_label_array label_array;
        uint64_t label_count;
-       uint64_t i;
 
        switch (bt_field_get_class_type(field)) {
        case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
@@ -692,31 +986,22 @@ int print_enum(struct pretty_component *pretty,
        }
 
        bt_common_g_string_append(pretty->string, "( ");
-       if (label_count == 0) {
-               if (pretty->use_colors) {
-                       bt_common_g_string_append(pretty->string, color_unknown);
-               }
-               bt_common_g_string_append(pretty->string, "<unknown>");
-               if (pretty->use_colors) {
-                       bt_common_g_string_append(pretty->string, color_rst);
-               }
-               goto skip_loop;
+       if (label_count != 0) {
+               /* The numerical value matches some labels, print them. */
+               print_enum_value_label_array(pretty, label_count, label_array);
+       } else if (pretty->options.print_enum_flags) {
+               /*
+                * The numerical value of the enumeration does not match any
+                * label but the `print-enum-flags` parameter is TRUE so try to
+                * decompose the value of the enum into bits and print it as a
+                * bit flag enum.
+                */
+               print_enum_try_bit_flags(pretty, field);
+       } else {
+               print_enum_value_label_unknown(pretty);
        }
-       for (i = 0; i < label_count; i++) {
-               const char *mapping_name = label_array[i];
 
-               if (i != 0) {
-                       bt_common_g_string_append(pretty->string, ", ");
-               }
-               if (pretty->use_colors) {
-                       bt_common_g_string_append(pretty->string, color_enum_mapping_name);
-               }
-               print_escape_string(pretty, mapping_name);
-               if (pretty->use_colors) {
-                       bt_common_g_string_append(pretty->string, color_rst);
-               }
-       }
-skip_loop:
+       /* Print the actual value of the enum. */
        bt_common_g_string_append(pretty->string, " : container = ");
        ret = print_integer(pretty, field);
        if (ret != 0) {
index c00e6ac783670e70e514c2d497e00e9cc6e57132..ed241c3b39cac53722e878b30f54e6cd2e2bc796 100755 (executable)
@@ -21,7 +21,7 @@ data_dir="$BT_TESTS_DATADIR/plugins/sink.text.pretty"
 temp_stdout_expected_file=$(mktemp -t test_pretty_expected_stdout.XXXXXX)
 temp_stderr_expected="/dev/null"
 
-plan_tests 14
+plan_tests 31
 
 function compare_enum_sorted
 {
@@ -57,6 +57,7 @@ function run_test
                "-p" "value=$value"
                "-p" "enum-signed=$enum_signed"
                "-c" "sink.text.pretty"
+               "-p" "print-enum-flags=true"
        )
 
        actual_stdout_file="$(mktemp -t actual_pretty_stdout.XXXXXX)"
@@ -101,6 +102,18 @@ function test_normal_enum {
        END
        run_test "$test_name" 0 7 "$temp_stdout_expected_file"
 
+       # Hit a range and a value
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( { "single3", "range" } : container = 4 ) }
+       END
+       run_test "$test_name" 0 4 "$temp_stdout_expected_file"
+
+       # Hit a range and a value
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( { "NOT_A_LABEL", "DOESNT_EXIST" } : container = 4 ) }
+       END
+       run_test "$test_name" 1 4 "$temp_stdout_expected_file"
+
        # Unknown
        cat <<- 'END' > "$temp_stdout_expected_file"
        with_enum: { enum_field = ( <unknown> : container = 21 ) }
@@ -150,10 +163,108 @@ function test_normal_enum_negative {
        run_test "$test_name" 0 0 "$temp_stdout_expected_file"
 }
 
+function test_bit_flag_enum {
+       test_name="Bit flag enum"
+       enum_signed="false"
+       enum_values="bit0,1,1 bit0bis,1,1 bit1,2,2 bit3,4,4 bit4,8,8 bit5,16,16 bit5,32,32"
+
+       # Single value hit
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit1" : container = 2 ) }
+       END
+       run_test "$test_name" 0 2 "$temp_stdout_expected_file"
+
+       # Multiple flags set
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit3" | "bit4" : container = 12 ) }
+       END
+       run_test "$test_name" 0 12 "$temp_stdout_expected_file"
+
+       # Some unknown bit
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = 68 ) }
+       END
+       run_test "$test_name" 0 68 "$temp_stdout_expected_file"
+
+       # Multiple labels for bit 0
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( { "bit0", "bit0bis" } : container = 1 ) }
+       END
+       run_test "$test_name" 0 1 "$temp_stdout_expected_file"
+
+       # Two labels for bit 0 and one label for bit 1
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( { "bit0", "bit0bis" } | "bit1" : container = 3 ) }
+       END
+       run_test "$test_name" 0 3 "$temp_stdout_expected_file"
+
+       # Single label for bit 0
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit5" | "bit5" : container = 48 ) }
+       END
+       run_test "$test_name" 0 48 "$temp_stdout_expected_file"
+
+       # negative value
+       enum_signed="true"
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = -1 ) }
+       END
+       run_test "$test_name" 0 -1 "$temp_stdout_expected_file"
+}
+
+function test_mixed_enum {
+       test_name="Mixed enum bits at beginning"
+       enum_signed="false"
+       enum_values="bit0,1,1 bit1,2,2 bit2,4,4 bit3,8,8 bit4,16,16 range,32,44 singleValue,45,45"
+
+       # Value with bit fields
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit0" | "bit1" | "bit2" | "bit3" | "bit4" : container = 31 ) }
+       END
+       run_test "$test_name" 0 31 "$temp_stdout_expected_file"
+
+       # A value with some bit flags set, but within another range
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "range" : container = 36 ) }
+       END
+       run_test "$test_name" 0 36 "$temp_stdout_expected_file"
+
+       # A value with some bit flags set, but corresponding to another value
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "singleValue" : container = 45 ) }
+       END
+       run_test "$test_name" 0 45 "$temp_stdout_expected_file"
+
+       # Value above the ranges
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = 46 ) }
+       END
+       run_test "$test_name" 0 46 "$temp_stdout_expected_file"
+
+       # Since low values are often powers of 2, they may be considered bit flags too
+       test_name="Mixed enum bits at end"
+       enum_signed="false"
+       enum_values="val1,1,1 val2,2,2 val3,3,3 val4,4,4 val5,5,5 bit3,8,8 bit4,16,16 bit5,32,32"
+
+       # Value with bit fields
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit4" : container = 16 ) }
+       END
+       run_test "$test_name" 0 16 "$temp_stdout_expected_file"
+
+       # Value with a bit field set both at beginning and end
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "val1" | "bit4" : container = 17 ) }
+       END
+       run_test "$test_name" 0 17 "$temp_stdout_expected_file"
+}
+
 # Enumerations tests
 test_normal_enum "false"
 test_normal_enum "true"
 test_normal_enum_negative
+test_bit_flag_enum
+test_mixed_enum
 
 # Do not `rm` $temp_stderr_expected because it's set to `/dev/null` right now
 # and that would print an error.
This page took 0.031945 seconds and 4 git commands to generate.