package nxm.sys.test.junit.lib;

import nxm.sys.lib.*;
import nxm.sys.test.junit.NMTestCase;

/** Unit tests for MaskValue.
    @author  J. Czechowski IV
    @version $Id: MaskValueTest.java,v 1.3 2013/05/21 20:50:44 ntn Exp $
 */
public class MaskValueTest extends NMTestCase {
  /** Public constructor to allow running this test case directly with JUnit.
      @param methodName The name of the method being called.
   */
  public MaskValueTest (String methodName) {
    this(methodName, null);
  }

  /** This constructor is required by NeXtMidas Unit Testing framework.
      @param methodName The name of the method being called.
      @param midas      The midas context to use when running the tests.
   */
  public MaskValueTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

  public static enum Enum16 { BIT1,  BIT2,  BIT3,  BIT4,  BIT5,  BIT6,  BIT7,  BIT8,
                              BIT9,  BIT10, BIT11, BIT12, BIT13, BIT14, BIT15, BIT16 };
  public static enum Enum32 { BIT1,  BIT2,  BIT3,  BIT4,  BIT5,  BIT6,  BIT7,  BIT8,
                              BIT9,  BIT10, BIT11, BIT12, BIT13, BIT14, BIT15, BIT16,
                              BIT17, BIT18, BIT19, BIT20, BIT21, BIT22, BIT23, BIT24,
                              BIT25, BIT26, BIT27, BIT28, BIT29, BIT30, BIT31, BIT32 };
  public static enum Enum40 { BIT1,  BIT2,  BIT3,  BIT4,  BIT5,  BIT6,  BIT7,  BIT8,
                              BIT9,  BIT10, BIT11, BIT12, BIT13, BIT14, BIT15, BIT16,
                              BIT17, BIT18, BIT19, BIT20, BIT21, BIT22, BIT23, BIT24,
                              BIT25, BIT26, BIT27, BIT28, BIT29, BIT30, BIT31, BIT32,
                              BIT33, BIT34, BIT35, BIT36, BIT37, BIT38, BIT39, BIT40 };
  public static enum Enum64 { BIT1,  BIT2,  BIT3,  BIT4,  BIT5,  BIT6,  BIT7,  BIT8,
                              BIT9,  BIT10, BIT11, BIT12, BIT13, BIT14, BIT15, BIT16,
                              BIT17, BIT18, BIT19, BIT20, BIT21, BIT22, BIT23, BIT24,
                              BIT25, BIT26, BIT27, BIT28, BIT29, BIT30, BIT31, BIT32,
                              BIT33, BIT34, BIT35, BIT36, BIT37, BIT38, BIT39, BIT40,
                              BIT41, BIT42, BIT43, BIT44, BIT45, BIT46, BIT47, BIT48,
                              BIT49, BIT50, BIT51, BIT52, BIT53, BIT54, BIT55, BIT56,
                              BIT57, BIT58, BIT59, BIT60, BIT61, BIT62, BIT63, BIT64 };
  public static enum Enum80 { BIT1,  BIT2,  BIT3,  BIT4,  BIT5,  BIT6,  BIT7,  BIT8,
                              BIT9,  BIT10, BIT11, BIT12, BIT13, BIT14, BIT15, BIT16,
                              BIT17, BIT18, BIT19, BIT20, BIT21, BIT22, BIT23, BIT24,
                              BIT25, BIT26, BIT27, BIT28, BIT29, BIT30, BIT31, BIT32,
                              BIT33, BIT34, BIT35, BIT36, BIT37, BIT38, BIT39, BIT40,
                              BIT41, BIT42, BIT43, BIT44, BIT45, BIT46, BIT47, BIT48,
                              BIT49, BIT50, BIT51, BIT52, BIT53, BIT54, BIT55, BIT56,
                              BIT57, BIT58, BIT59, BIT60, BIT61, BIT62, BIT63, BIT64,
                              BIT65, BIT66, BIT67, BIT68, BIT69, BIT70, BIT71, BIT72,
                              BIT73, BIT74, BIT75, BIT76, BIT77, BIT78, BIT79, BIT80 };

  public static enum Flags  { Permanent,Sticky,Fixed,NoEdit,NoMsg,InLine,VBox,VBoxMO };


  public void testClone () {
    MaskValue<Enum16> mv16 = MaskValue.constant(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.constant(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.constant(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.constant(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.constant(Enum80.BIT2, Enum80.BIT4);

    assertEquals("clone() is equal", mv16, mv16.clone());
    assertEquals("clone() is equal", mv32, mv32.clone());
    assertEquals("clone() is equal", mv40, mv40.clone());
    assertEquals("clone() is equal", mv64, mv64.clone());
    assertEquals("clone() is equal", mv80, mv80.clone());

    Exception e16 = null;
    Exception e32 = null;
    Exception e40 = null;
    Exception e64 = null;
    Exception e80 = null;

    try { mv16.clone().setNone(); } catch (Exception e) { e16 = e; }
    try { mv32.clone().setNone(); } catch (Exception e) { e32 = e; }
    try { mv40.clone().setNone(); } catch (Exception e) { e40 = e; }
    try { mv64.clone().setNone(); } catch (Exception e) { e64 = e; }
    try { mv80.clone().setNone(); } catch (Exception e) { e80 = e; }

    assertNotNull("Caught exception trying to modify clone()", e16);
    assertNotNull("Caught exception trying to modify clone()", e32);
    assertNotNull("Caught exception trying to modify clone()", e40);
    assertNotNull("Caught exception trying to modify clone()", e64);
    assertNotNull("Caught exception trying to modify clone()", e80);
  }

  public void testCopy () {
    MaskValue<Enum16> mv16 = MaskValue.constant(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.constant(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.constant(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.constant(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.constant(Enum80.BIT2, Enum80.BIT4);

    assertEquals("copy() is equal", mv16, mv16.copy());
    assertEquals("copy() is equal", mv32, mv32.copy());
    assertEquals("copy() is equal", mv40, mv40.copy());
    assertEquals("copy() is equal", mv64, mv64.copy());
    assertEquals("copy() is equal", mv80, mv80.copy());

    Exception e16 = null;
    Exception e32 = null;
    Exception e40 = null;
    Exception e64 = null;
    Exception e80 = null;

    try { mv16.copy().setNone(); } catch (Exception e) { e16 = e; }
    try { mv32.copy().setNone(); } catch (Exception e) { e32 = e; }
    try { mv40.copy().setNone(); } catch (Exception e) { e40 = e; }
    try { mv64.copy().setNone(); } catch (Exception e) { e64 = e; }
    try { mv80.copy().setNone(); } catch (Exception e) { e80 = e; }

    assertNull("No exception trying to modify copy()", e16);
    assertNull("No exception trying to modify copy()", e32);
    assertNull("No exception trying to modify copy()", e40);
    assertNull("No exception trying to modify copy()", e64);
    assertNull("No exception trying to modify copy()", e80);
  }

  public void testForType () {
    // ==== WITH DEFAULT VALUES ====
    MaskValue<Enum16> mv16 = MaskValue.forType(Enum16.class, Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.forType(Enum32.class, Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.forType(Enum40.class, Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.forType(Enum64.class, Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.forType(Enum80.class, Enum80.BIT2, Enum80.BIT4);

    // Class names is private, but check it here just to be safe.
    assertEquals("Correct class for Enum16", "nxm.sys.lib.MaskValue",                 mv16.getClass().getName());
    assertEquals("Correct class for Enum32", "nxm.sys.lib.MaskValue",                 mv32.getClass().getName());
    assertEquals("Correct class for Enum40", "nxm.sys.lib.MaskValue$MaskValueLong",   mv40.getClass().getName());
    assertEquals("Correct class for Enum64", "nxm.sys.lib.MaskValue$MaskValueLong",   mv64.getClass().getName());
    assertEquals("Correct class for Enum80", "nxm.sys.lib.MaskValue$MaskValueBitSet", mv80.getClass().getName());

    // Check to toString to verify the values were set properly.
    assertEquals("Correct values for Enum16", "|BIT2|BIT4|", mv16.toString());
    assertEquals("Correct values for Enum32", "|BIT2|BIT4|", mv32.toString());
    assertEquals("Correct values for Enum40", "|BIT2|BIT4|", mv40.toString());
    assertEquals("Correct values for Enum64", "|BIT2|BIT4|", mv64.toString());
    assertEquals("Correct values for Enum80", "|BIT2|BIT4|", mv80.toString());

    // Check to see that we can modify values
    mv16.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
    mv32.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
    mv40.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
    mv64.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
    mv80.setNone(); // <-- would throw an exception if constant (no exception -> PASS)

    // ==== NO DEFAULT VALUES ====
    mv16 = MaskValue.forType(Enum16.class);
    mv32 = MaskValue.forType(Enum32.class);
    mv40 = MaskValue.forType(Enum40.class);
    mv64 = MaskValue.forType(Enum64.class);
    mv80 = MaskValue.forType(Enum80.class);

    // Class names is private, but check it here just to be safe.
    assertEquals("Correct class for Enum16", "nxm.sys.lib.MaskValue",                 mv16.getClass().getName());
    assertEquals("Correct class for Enum32", "nxm.sys.lib.MaskValue",                 mv32.getClass().getName());
    assertEquals("Correct class for Enum40", "nxm.sys.lib.MaskValue$MaskValueLong",   mv40.getClass().getName());
    assertEquals("Correct class for Enum64", "nxm.sys.lib.MaskValue$MaskValueLong",   mv64.getClass().getName());
    assertEquals("Correct class for Enum80", "nxm.sys.lib.MaskValue$MaskValueBitSet", mv80.getClass().getName());

    // Check to toString to verify the values were set properly.
    assertEquals("Correct values for Enum16", "|", mv16.toString());
    assertEquals("Correct values for Enum32", "|", mv32.toString());
    assertEquals("Correct values for Enum40", "|", mv40.toString());
    assertEquals("Correct values for Enum64", "|", mv64.toString());
    assertEquals("Correct values for Enum80", "|", mv80.toString());
  }

//  REMOVING THIS TEST IN 4.0.0 - TESTS FOR LEGACY FIELD & HAS LIBG DEPENDENCY
//  public void testForLegacyField () {
//    nxm.sys.libg.GWidget self = new nxm.sys.libg.GWidget("Name", null, "Title", 0, null);
//
//    MaskValue<Flags> flagsMaskValue = MaskValue.forLegacyField(self, "flags", Flags.class);
//
//    assertEquals("Correct value for Legacy field:","|",flagsMaskValue.toString());
//
//    flagsMaskValue.set(Flags.NoMsg);
//    assertEquals("Correct value for Legacy field:","|NoMsg|",flagsMaskValue.toString());
//
//    // Test below commented out since it relies on deprecated behaviour in GWidget
//    // self.flags = 0xF;
//    // assertEquals("Correct value for Legacy field:","|Permanent|Sticky|Fixed|NoEdit|",flagsMaskValue.toString());
//
//  }

  public void testWith () {
    MaskValue<Enum16> mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    // Class names is private, but check it here just to be safe.
    assertEquals("Correct class for Enum16", "nxm.sys.lib.MaskValue",                 mv16.getClass().getName());
    assertEquals("Correct class for Enum32", "nxm.sys.lib.MaskValue",                 mv32.getClass().getName());
    assertEquals("Correct class for Enum40", "nxm.sys.lib.MaskValue$MaskValueLong",   mv40.getClass().getName());
    assertEquals("Correct class for Enum64", "nxm.sys.lib.MaskValue$MaskValueLong",   mv64.getClass().getName());
    assertEquals("Correct class for Enum80", "nxm.sys.lib.MaskValue$MaskValueBitSet", mv80.getClass().getName());

    // Check to toString to verify the values were set properly.
    assertEquals("Correct values for Enum16", "|BIT2|BIT4|", mv16.toString());
    assertEquals("Correct values for Enum32", "|BIT2|BIT4|", mv32.toString());
    assertEquals("Correct values for Enum40", "|BIT2|BIT4|", mv40.toString());
    assertEquals("Correct values for Enum64", "|BIT2|BIT4|", mv64.toString());
    assertEquals("Correct values for Enum80", "|BIT2|BIT4|", mv80.toString());

    // Check to see that we can modify values
    mv16.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
    mv32.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
    mv40.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
    mv64.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
    mv80.setNone(); // <-- would throw an exception if constant (no exception -> PASS)
  }

  public void testConstant () {
    // ==== WITH DEFAULT VALUES ====
    MaskValue<Enum16> mv16 = MaskValue.constant(Enum16.class, Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.constant(Enum32.class, Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.constant(Enum40.class, Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.constant(Enum64.class, Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.constant(Enum80.class, Enum80.BIT2, Enum80.BIT4);

    // Class names is private, but check it here just to be safe.
    assertEquals("Correct class for Enum16", "nxm.sys.lib.MaskValue",                 mv16.getClass().getName());
    assertEquals("Correct class for Enum32", "nxm.sys.lib.MaskValue",                 mv32.getClass().getName());
    assertEquals("Correct class for Enum40", "nxm.sys.lib.MaskValue$MaskValueLong",   mv40.getClass().getName());
    assertEquals("Correct class for Enum64", "nxm.sys.lib.MaskValue$MaskValueLong",   mv64.getClass().getName());
    assertEquals("Correct class for Enum80", "nxm.sys.lib.MaskValue$MaskValueBitSet", mv80.getClass().getName());

    // Check to toString to verify the values were set properly.
    assertEquals("Correct values for Enum16", "|BIT2|BIT4|", mv16.toString());
    assertEquals("Correct values for Enum32", "|BIT2|BIT4|", mv32.toString());
    assertEquals("Correct values for Enum40", "|BIT2|BIT4|", mv40.toString());
    assertEquals("Correct values for Enum64", "|BIT2|BIT4|", mv64.toString());
    assertEquals("Correct values for Enum80", "|BIT2|BIT4|", mv80.toString());

    // Check to see that we can modify values
    Exception e16 = null;
    Exception e32 = null;
    Exception e40 = null;
    Exception e64 = null;
    Exception e80 = null;

    try { mv16.setNone(); } catch (Exception e) { e16 = e; }
    try { mv32.setNone(); } catch (Exception e) { e32 = e; }
    try { mv40.setNone(); } catch (Exception e) { e40 = e; }
    try { mv64.setNone(); } catch (Exception e) { e64 = e; }
    try { mv80.setNone(); } catch (Exception e) { e80 = e; }

    assertNotNull("Caught exception trying to modify constant value with Enum16", e16);
    assertNotNull("Caught exception trying to modify constant value with Enum32", e32);
    assertNotNull("Caught exception trying to modify constant value with Enum40", e40);
    assertNotNull("Caught exception trying to modify constant value with Enum64", e64);
    assertNotNull("Caught exception trying to modify constant value with Enum80", e80);

    // ==== NO TYPE WITH DEFAULT VALUES ====
    mv16 = MaskValue.constant(Enum16.BIT2, Enum16.BIT4);
    mv32 = MaskValue.constant(Enum32.BIT2, Enum32.BIT4);
    mv40 = MaskValue.constant(Enum40.BIT2, Enum40.BIT4);
    mv64 = MaskValue.constant(Enum64.BIT2, Enum64.BIT4);
    mv80 = MaskValue.constant(Enum80.BIT2, Enum80.BIT4);

    // Class names is private, but check it here just to be safe.
    assertEquals("Correct class for Enum16", "nxm.sys.lib.MaskValue",                 mv16.getClass().getName());
    assertEquals("Correct class for Enum32", "nxm.sys.lib.MaskValue",                 mv32.getClass().getName());
    assertEquals("Correct class for Enum40", "nxm.sys.lib.MaskValue$MaskValueLong",   mv40.getClass().getName());
    assertEquals("Correct class for Enum64", "nxm.sys.lib.MaskValue$MaskValueLong",   mv64.getClass().getName());
    assertEquals("Correct class for Enum80", "nxm.sys.lib.MaskValue$MaskValueBitSet", mv80.getClass().getName());

    // Check to toString to verify the values were set properly.
    assertEquals("Correct values for Enum16", "|BIT2|BIT4|", mv16.toString());
    assertEquals("Correct values for Enum32", "|BIT2|BIT4|", mv32.toString());
    assertEquals("Correct values for Enum40", "|BIT2|BIT4|", mv40.toString());
    assertEquals("Correct values for Enum64", "|BIT2|BIT4|", mv64.toString());
    assertEquals("Correct values for Enum80", "|BIT2|BIT4|", mv80.toString());

    // Check to see that we can modify values
    e16 = null;
    e32 = null;
    e40 = null;
    e64 = null;
    e80 = null;

    try { mv16.setNone(); } catch (Exception e) { e16 = e; }
    try { mv32.setNone(); } catch (Exception e) { e32 = e; }
    try { mv40.setNone(); } catch (Exception e) { e40 = e; }
    try { mv64.setNone(); } catch (Exception e) { e64 = e; }
    try { mv80.setNone(); } catch (Exception e) { e80 = e; }

    assertNotNull("Caught exception trying to modify constant value with Enum16", e16);
    assertNotNull("Caught exception trying to modify constant value with Enum32", e32);
    assertNotNull("Caught exception trying to modify constant value with Enum40", e40);
    assertNotNull("Caught exception trying to modify constant value with Enum64", e64);
    assertNotNull("Caught exception trying to modify constant value with Enum80", e80);

    // ==== NO DEFAULT VALUES ====
    mv16 = MaskValue.constant(Enum16.class);
    mv32 = MaskValue.constant(Enum32.class);
    mv40 = MaskValue.constant(Enum40.class);
    mv64 = MaskValue.constant(Enum64.class);
    mv80 = MaskValue.constant(Enum80.class);

    // Class names is private, but check it here just to be safe.
    assertEquals("Correct class for Enum16", "nxm.sys.lib.MaskValue",                 mv16.getClass().getName());
    assertEquals("Correct class for Enum32", "nxm.sys.lib.MaskValue",                 mv32.getClass().getName());
    assertEquals("Correct class for Enum40", "nxm.sys.lib.MaskValue$MaskValueLong",   mv40.getClass().getName());
    assertEquals("Correct class for Enum64", "nxm.sys.lib.MaskValue$MaskValueLong",   mv64.getClass().getName());
    assertEquals("Correct class for Enum80", "nxm.sys.lib.MaskValue$MaskValueBitSet", mv80.getClass().getName());

    // Check to toString to verify the values were set properly.
    assertEquals("Correct values for Enum16", "|", mv16.toString());
    assertEquals("Correct values for Enum32", "|", mv32.toString());
    assertEquals("Correct values for Enum40", "|", mv40.toString());
    assertEquals("Correct values for Enum64", "|", mv64.toString());
    assertEquals("Correct values for Enum80", "|", mv80.toString());

    // Check to see that we can modify values
    e16 = null;
    e32 = null;
    e40 = null;
    e64 = null;
    e80 = null;

    try { mv16.setNone(); } catch (Exception e) { e16 = e; }
    try { mv32.setNone(); } catch (Exception e) { e32 = e; }
    try { mv40.setNone(); } catch (Exception e) { e40 = e; }
    try { mv64.setNone(); } catch (Exception e) { e64 = e; }
    try { mv80.setNone(); } catch (Exception e) { e80 = e; }

    assertNotNull("Caught exception trying to modify constant value with Enum16", e16);
    assertNotNull("Caught exception trying to modify constant value with Enum32", e32);
    assertNotNull("Caught exception trying to modify constant value with Enum40", e40);
    assertNotNull("Caught exception trying to modify constant value with Enum64", e64);
    assertNotNull("Caught exception trying to modify constant value with Enum80", e80);
  }

  public void testEquals () {
    MaskValue<Enum16> mv16   = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32   = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40   = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64   = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80   = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    MaskValue<Enum16> same16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> same32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> same40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> same64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> same80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    MaskValue<Enum16> diff16 = MaskValue.with(Enum16.BIT4, Enum16.BIT8);
    MaskValue<Enum32> diff32 = MaskValue.with(Enum32.BIT4, Enum32.BIT8);
    MaskValue<Enum40> diff40 = MaskValue.with(Enum40.BIT4, Enum40.BIT8);
    MaskValue<Enum64> diff64 = MaskValue.with(Enum64.BIT4, Enum64.BIT8);
    MaskValue<Enum80> diff80 = MaskValue.with(Enum80.BIT4, Enum80.BIT8);

    assertFalse("Equals with different object", mv16.equals("BIT2|BIT4"));
    assertFalse("Equals with different type",   mv16.equals(mv32));  // same class
    assertFalse("Equals with different type",   mv16.equals(mv40));  // diff class
    assertFalse("Equals with different type",   mv40.equals(mv64));  // same class
    assertFalse("Equals with different type",   mv64.equals(mv80));  // diff class

    assertTrue("Equals with self", mv16.equals(mv16));
    assertTrue("Equals with self", mv32.equals(mv32));
    assertTrue("Equals with self", mv40.equals(mv40));
    assertTrue("Equals with self", mv64.equals(mv64));
    assertTrue("Equals with self", mv80.equals(mv80));

    assertTrue("Equals with same", same16.equals(mv16));
    assertTrue("Equals with same", same32.equals(mv32));
    assertTrue("Equals with same", same40.equals(mv40));
    assertTrue("Equals with same", same64.equals(mv64));
    assertTrue("Equals with same", same80.equals(mv80));

    assertFalse("Equals with diff", diff16.equals(mv16));
    assertFalse("Equals with diff", diff32.equals(mv32));
    assertFalse("Equals with diff", diff40.equals(mv40));
    assertFalse("Equals with diff", diff64.equals(mv64));
    assertFalse("Equals with diff", diff80.equals(mv80));
  }

  public void testHashCode () {
    MaskValue<Enum16> mv16   = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32   = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40   = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64   = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80   = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    MaskValue<Enum16> same16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> same32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> same40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> same64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> same80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    assertEquals("Hash code for self (Enum16)", mv16.hashCode(), mv16.hashCode());
    assertEquals("Hash code for self (Enum32)", mv32.hashCode(), mv32.hashCode());
    assertEquals("Hash code for self (Enum40)", mv40.hashCode(), mv40.hashCode());
    assertEquals("Hash code for self (Enum64)", mv64.hashCode(), mv64.hashCode());
    assertEquals("Hash code for self (Enum80)", mv80.hashCode(), mv80.hashCode());

    assertEquals("Hash code for same (Enum16)", same16.hashCode(), mv16.hashCode());
    assertEquals("Hash code for same (Enum32)", same32.hashCode(), mv32.hashCode());
    assertEquals("Hash code for same (Enum40)", same40.hashCode(), mv40.hashCode());
    assertEquals("Hash code for same (Enum64)", same64.hashCode(), mv64.hashCode());
    assertEquals("Hash code for same (Enum80)", same80.hashCode(), mv80.hashCode());
  }

  public void testToString () {
    // None Set
    assertEquals("mv16.toString()", "|", MaskValue.forType(Enum16.class).toString());
    assertEquals("mv32.toString()", "|", MaskValue.forType(Enum32.class).toString());
    assertEquals("mv40.toString()", "|", MaskValue.forType(Enum40.class).toString());
    assertEquals("mv64.toString()", "|", MaskValue.forType(Enum64.class).toString());
    assertEquals("mv80.toString()", "|", MaskValue.forType(Enum80.class).toString());

    // Single Element
    for (Enum16 e : Enum16.values()) assertEquals("mv16.toString()", "|"+e+"|", MaskValue.with(e).toString());
    for (Enum32 e : Enum32.values()) assertEquals("mv32.toString()", "|"+e+"|", MaskValue.with(e).toString());
    for (Enum40 e : Enum40.values()) assertEquals("mv40.toString()", "|"+e+"|", MaskValue.with(e).toString());
    for (Enum64 e : Enum64.values()) assertEquals("mv64.toString()", "|"+e+"|", MaskValue.with(e).toString());
    for (Enum80 e : Enum80.values()) assertEquals("mv80.toString()", "|"+e+"|", MaskValue.with(e).toString());

    // Multiple Elements
    assertEquals("mv16.toString()", "|BIT2|BIT4|BIT6|BIT8|", MaskValue.with(Enum16.BIT2, Enum16.BIT4, Enum16.BIT6, Enum16.BIT8).toString());
    assertEquals("mv32.toString()", "|BIT2|BIT4|BIT6|BIT8|", MaskValue.with(Enum32.BIT2, Enum32.BIT4, Enum32.BIT6, Enum32.BIT8).toString());
    assertEquals("mv40.toString()", "|BIT2|BIT4|BIT6|BIT8|", MaskValue.with(Enum40.BIT2, Enum40.BIT4, Enum40.BIT6, Enum40.BIT8).toString());
    assertEquals("mv64.toString()", "|BIT2|BIT4|BIT6|BIT8|", MaskValue.with(Enum64.BIT2, Enum64.BIT4, Enum64.BIT6, Enum64.BIT8).toString());
    assertEquals("mv80.toString()", "|BIT2|BIT4|BIT6|BIT8|", MaskValue.with(Enum80.BIT2, Enum80.BIT4, Enum80.BIT6, Enum80.BIT8).toString());
  }

  public void testIsSet () {
    MaskValue<Enum16> mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    // ==== WITH ENUM ====
    for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", ((e == Enum16.BIT2) || (e == Enum16.BIT4)), mv16.isSet(e));
    for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", ((e == Enum32.BIT2) || (e == Enum32.BIT4)), mv32.isSet(e));
    for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", ((e == Enum40.BIT2) || (e == Enum40.BIT4)), mv40.isSet(e));
    for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", ((e == Enum64.BIT2) || (e == Enum64.BIT4)), mv64.isSet(e));
    for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", ((e == Enum80.BIT2) || (e == Enum80.BIT4)), mv80.isSet(e));

    // ==== WITH STRING ====
    for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", ((e == Enum16.BIT2) || (e == Enum16.BIT4)), mv16.isSet(e.toString()));
    for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", ((e == Enum32.BIT2) || (e == Enum32.BIT4)), mv32.isSet(e.toString()));
    for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", ((e == Enum40.BIT2) || (e == Enum40.BIT4)), mv40.isSet(e.toString()));
    for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", ((e == Enum64.BIT2) || (e == Enum64.BIT4)), mv64.isSet(e.toString()));
    for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", ((e == Enum80.BIT2) || (e == Enum80.BIT4)), mv80.isSet(e.toString()));

    // ==== WITH MASKVALUE ====
    for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", ((e == Enum16.BIT2) || (e == Enum16.BIT4)), mv16.isSet(MaskValue.with(e)));
    for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", ((e == Enum32.BIT2) || (e == Enum32.BIT4)), mv32.isSet(MaskValue.with(e)));
    for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", ((e == Enum40.BIT2) || (e == Enum40.BIT4)), mv40.isSet(MaskValue.with(e)));
    for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", ((e == Enum64.BIT2) || (e == Enum64.BIT4)), mv64.isSet(MaskValue.with(e)));
    for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", ((e == Enum80.BIT2) || (e == Enum80.BIT4)), mv80.isSet(MaskValue.with(e)));

    assertTrue("mv16.isSet(..)", mv16.isSet(MaskValue.with(Enum16.BIT2, Enum16.BIT4)));
    assertTrue("mv32.isSet(..)", mv32.isSet(MaskValue.with(Enum32.BIT2, Enum32.BIT4)));
    assertTrue("mv40.isSet(..)", mv40.isSet(MaskValue.with(Enum40.BIT2, Enum40.BIT4)));
    assertTrue("mv64.isSet(..)", mv64.isSet(MaskValue.with(Enum64.BIT2, Enum64.BIT4)));
    assertTrue("mv80.isSet(..)", mv80.isSet(MaskValue.with(Enum80.BIT2, Enum80.BIT4)));

    assertFalse("mv16.isSet(..)", mv16.isSet(MaskValue.with(Enum16.BIT2, Enum16.BIT4, Enum16.BIT6)));
    assertFalse("mv32.isSet(..)", mv32.isSet(MaskValue.with(Enum32.BIT2, Enum32.BIT4, Enum32.BIT6)));
    assertFalse("mv40.isSet(..)", mv40.isSet(MaskValue.with(Enum40.BIT2, Enum40.BIT4, Enum40.BIT6)));
    assertFalse("mv64.isSet(..)", mv64.isSet(MaskValue.with(Enum64.BIT2, Enum64.BIT4, Enum64.BIT6)));
    assertFalse("mv80.isSet(..)", mv80.isSet(MaskValue.with(Enum80.BIT2, Enum80.BIT4, Enum80.BIT6)));

    assertFalse("mv16.isSet(..)", mv16.isSet(MaskValue.with(Enum16.BIT2, Enum16.BIT6)));
    assertFalse("mv32.isSet(..)", mv32.isSet(MaskValue.with(Enum32.BIT2, Enum32.BIT6)));
    assertFalse("mv40.isSet(..)", mv40.isSet(MaskValue.with(Enum40.BIT2, Enum40.BIT6)));
    assertFalse("mv64.isSet(..)", mv64.isSet(MaskValue.with(Enum64.BIT2, Enum64.BIT6)));
    assertFalse("mv80.isSet(..)", mv80.isSet(MaskValue.with(Enum80.BIT2, Enum80.BIT6)));
  }

  public void testSet () {
    // ==== WITH ENUM ====
    for (Enum16 v : Enum16.values()) {
      MaskValue<Enum16> mv = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
      mv.set(v);
      for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")",    ((e == Enum16.BIT2) || (e == Enum16.BIT4) || (e == v)), mv.isSet(e));
      for (Enum16 e : Enum16.values()) assertEquals("mv16.getValue("+e+")", ((e == Enum16.BIT2) || (e == Enum16.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum32 v : Enum32.values()) {
      MaskValue<Enum32> mv = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
      mv.set(v);
      for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")",    ((e == Enum32.BIT2) || (e == Enum32.BIT4) || (e == v)), mv.isSet(e));
      for (Enum32 e : Enum32.values()) assertEquals("mv32.getValue("+e+")", ((e == Enum32.BIT2) || (e == Enum32.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum40 v : Enum40.values()) {
      MaskValue<Enum40> mv = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
      mv.set(v);
      for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")",    ((e == Enum40.BIT2) || (e == Enum40.BIT4) || (e == v)), mv.isSet(e));
      for (Enum40 e : Enum40.values()) assertEquals("mv40.getValue("+e+")", ((e == Enum40.BIT2) || (e == Enum40.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum64 v : Enum64.values()) {
      MaskValue<Enum64> mv = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
      mv.set(v);
      for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")",    ((e == Enum64.BIT2) || (e == Enum64.BIT4) || (e == v)), mv.isSet(e));
      for (Enum64 e : Enum64.values()) assertEquals("mv64.getValue("+e+")", ((e == Enum64.BIT2) || (e == Enum64.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum80 v : Enum80.values()) {
      MaskValue<Enum80> mv = MaskValue.with(Enum80.BIT2, Enum80.BIT4);
      mv.set(v);
      for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")",    ((e == Enum80.BIT2) || (e == Enum80.BIT4) || (e == v)), mv.isSet(e));
      for (Enum80 e : Enum80.values()) assertEquals("mv80.getValue("+e+")", ((e == Enum80.BIT2) || (e == Enum80.BIT4) || (e == v)), mv.getValue(e));
    }

    // ==== WITH STRING ====
    for (Enum16 v : Enum16.values()) {
      MaskValue<Enum16> mv = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
      mv.set(v.toString());
      for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")",    ((e == Enum16.BIT2) || (e == Enum16.BIT4) || (e == v)), mv.isSet(e));
      for (Enum16 e : Enum16.values()) assertEquals("mv16.getValue("+e+")", ((e == Enum16.BIT2) || (e == Enum16.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum32 v : Enum32.values()) {
      MaskValue<Enum32> mv = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
      mv.set(v.toString());
      for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")",    ((e == Enum32.BIT2) || (e == Enum32.BIT4) || (e == v)), mv.isSet(e));
      for (Enum32 e : Enum32.values()) assertEquals("mv32.getValue("+e+")", ((e == Enum32.BIT2) || (e == Enum32.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum40 v : Enum40.values()) {
      MaskValue<Enum40> mv = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
      mv.set(v.toString());
      for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")",    ((e == Enum40.BIT2) || (e == Enum40.BIT4) || (e == v)), mv.isSet(e));
      for (Enum40 e : Enum40.values()) assertEquals("mv40.getValue("+e+")", ((e == Enum40.BIT2) || (e == Enum40.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum64 v : Enum64.values()) {
      MaskValue<Enum64> mv = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
      mv.set(v.toString());
      for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")",    ((e == Enum64.BIT2) || (e == Enum64.BIT4) || (e == v)), mv.isSet(e));
      for (Enum64 e : Enum64.values()) assertEquals("mv64.getValue("+e+")", ((e == Enum64.BIT2) || (e == Enum64.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum80 v : Enum80.values()) {
      MaskValue<Enum80> mv = MaskValue.with(Enum80.BIT2, Enum80.BIT4);
      mv.set(v.toString());
      for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")",    ((e == Enum80.BIT2) || (e == Enum80.BIT4) || (e == v)), mv.isSet(e));
      for (Enum80 e : Enum80.values()) assertEquals("mv80.getValue("+e+")", ((e == Enum80.BIT2) || (e == Enum80.BIT4) || (e == v)), mv.getValue(e));
    }

    // ==== WITH MASKVALUE ====
    for (Enum16 v : Enum16.values()) {
      MaskValue<Enum16> mv = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
      mv.set(MaskValue.with(v));
      for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")",    ((e == Enum16.BIT2) || (e == Enum16.BIT4) || (e == v)), mv.isSet(e));
      for (Enum16 e : Enum16.values()) assertEquals("mv16.getValue("+e+")", ((e == Enum16.BIT2) || (e == Enum16.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum32 v : Enum32.values()) {
      MaskValue<Enum32> mv = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
      mv.set(MaskValue.with(v));
      for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")",    ((e == Enum32.BIT2) || (e == Enum32.BIT4) || (e == v)), mv.isSet(e));
      for (Enum32 e : Enum32.values()) assertEquals("mv32.getValue("+e+")", ((e == Enum32.BIT2) || (e == Enum32.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum40 v : Enum40.values()) {
      MaskValue<Enum40> mv = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
      mv.set(MaskValue.with(v));
      for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")",    ((e == Enum40.BIT2) || (e == Enum40.BIT4) || (e == v)), mv.isSet(e));
      for (Enum40 e : Enum40.values()) assertEquals("mv40.getValue("+e+")", ((e == Enum40.BIT2) || (e == Enum40.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum64 v : Enum64.values()) {
      MaskValue<Enum64> mv = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
      mv.set(MaskValue.with(v));
      for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")",    ((e == Enum64.BIT2) || (e == Enum64.BIT4) || (e == v)), mv.isSet(e));
      for (Enum64 e : Enum64.values()) assertEquals("mv64.getValue("+e+")", ((e == Enum64.BIT2) || (e == Enum64.BIT4) || (e == v)), mv.getValue(e));
    }
    for (Enum80 v : Enum80.values()) {
      MaskValue<Enum80> mv = MaskValue.with(Enum80.BIT2, Enum80.BIT4);
      mv.set(MaskValue.with(v));
      for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")",    ((e == Enum80.BIT2) || (e == Enum80.BIT4) || (e == v)), mv.isSet(e));
      for (Enum80 e : Enum80.values()) assertEquals("mv80.getValue("+e+")", ((e == Enum80.BIT2) || (e == Enum80.BIT4) || (e == v)), mv.getValue(e));
    }
  }

  public void testGetValue () { } // tested with isSet

  public void testUnset () {
    // ==== WITH ENUM ====
    for (Enum16 v : Enum16.values()) {
      MaskValue<Enum16> mv = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
      mv.unset(v);
      for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", (e != v) && ((e == Enum16.BIT2) || (e == Enum16.BIT4)), mv.isSet(e));
    }
    for (Enum32 v : Enum32.values()) {
      MaskValue<Enum32> mv = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
      mv.unset(v);
      for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", (e != v) && ((e == Enum32.BIT2) || (e == Enum32.BIT4)), mv.isSet(e));
    }
    for (Enum40 v : Enum40.values()) {
      MaskValue<Enum40> mv = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
      mv.unset(v);
      for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", (e != v) && ((e == Enum40.BIT2) || (e == Enum40.BIT4)), mv.isSet(e));
    }
    for (Enum64 v : Enum64.values()) {
      MaskValue<Enum64> mv = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
      mv.unset(v);
      for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", (e != v) && ((e == Enum64.BIT2) || (e == Enum64.BIT4)), mv.isSet(e));
    }
    for (Enum80 v : Enum80.values()) {
      MaskValue<Enum80> mv = MaskValue.with(Enum80.BIT2, Enum80.BIT4);
      mv.unset(v);
      for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", (e != v) && ((e == Enum80.BIT2) || (e == Enum80.BIT4)), mv.isSet(e));
    }

    // ==== WITH STRING ====
    for (Enum16 v : Enum16.values()) {
      MaskValue<Enum16> mv = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
      mv.unset(v.toString());
      for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", (e != v) && ((e == Enum16.BIT2) || (e == Enum16.BIT4)), mv.isSet(e));
    }
    for (Enum32 v : Enum32.values()) {
      MaskValue<Enum32> mv = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
      mv.unset(v.toString());
      for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", (e != v) && ((e == Enum32.BIT2) || (e == Enum32.BIT4)), mv.isSet(e));
    }
    for (Enum40 v : Enum40.values()) {
      MaskValue<Enum40> mv = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
      mv.unset(v.toString());
      for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", (e != v) && ((e == Enum40.BIT2) || (e == Enum40.BIT4)), mv.isSet(e));
    }
    for (Enum64 v : Enum64.values()) {
      MaskValue<Enum64> mv = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
      mv.unset(v.toString());
      for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", (e != v) && ((e == Enum64.BIT2) || (e == Enum64.BIT4)), mv.isSet(e));
    }
    for (Enum80 v : Enum80.values()) {
      MaskValue<Enum80> mv = MaskValue.with(Enum80.BIT2, Enum80.BIT4);
      mv.unset(v.toString());
      for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", (e != v) && ((e == Enum80.BIT2) || (e == Enum80.BIT4)), mv.isSet(e));
    }

    // ==== WITH MASKVALUE ====
    for (Enum16 v : Enum16.values()) {
      MaskValue<Enum16> mv = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
      mv.unset(MaskValue.with(v));
      for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", (e != v) && ((e == Enum16.BIT2) || (e == Enum16.BIT4)), mv.isSet(e));
    }
    for (Enum32 v : Enum32.values()) {
      MaskValue<Enum32> mv = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
      mv.unset(MaskValue.with(v));
      for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", (e != v) && ((e == Enum32.BIT2) || (e == Enum32.BIT4)), mv.isSet(e));
    }
    for (Enum40 v : Enum40.values()) {
      MaskValue<Enum40> mv = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
      mv.unset(MaskValue.with(v));
      for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", (e != v) && ((e == Enum40.BIT2) || (e == Enum40.BIT4)), mv.isSet(e));
    }
    for (Enum64 v : Enum64.values()) {
      MaskValue<Enum64> mv = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
      mv.unset(MaskValue.with(v));
      for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", (e != v) && ((e == Enum64.BIT2) || (e == Enum64.BIT4)), mv.isSet(e));
    }
    for (Enum80 v : Enum80.values()) {
      MaskValue<Enum80> mv = MaskValue.with(Enum80.BIT2, Enum80.BIT4);
      mv.unset(MaskValue.with(v));
      for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", (e != v) && ((e == Enum80.BIT2) || (e == Enum80.BIT4)), mv.isSet(e));
    }
  }

  public synchronized void testToggle () {
    // ==== WITH ENUM & STRING ====
    MaskValue<Enum16> mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);  mv16.toggle(Enum16.BIT4);  mv16.toggle("BIT8");
    MaskValue<Enum32> mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);  mv32.toggle(Enum32.BIT4);  mv32.toggle("BIT8");
    MaskValue<Enum40> mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);  mv40.toggle(Enum40.BIT4);  mv40.toggle("BIT8");
    MaskValue<Enum64> mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);  mv64.toggle(Enum64.BIT4);  mv64.toggle("BIT8");
    MaskValue<Enum80> mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);  mv80.toggle(Enum80.BIT4);  mv80.toggle("BIT8");

    for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", (e == Enum16.BIT2) || (e == Enum16.BIT8), mv16.isSet(e));
    for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", (e == Enum32.BIT2) || (e == Enum32.BIT8), mv32.isSet(e));
    for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", (e == Enum40.BIT2) || (e == Enum40.BIT8), mv40.isSet(e));
    for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", (e == Enum64.BIT2) || (e == Enum64.BIT8), mv64.isSet(e));
    for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", (e == Enum80.BIT2) || (e == Enum80.BIT8), mv80.isSet(e));

    // ==== WITH MASKVALUE ====
    mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    mv16.toggle(MaskValue.with(Enum16.BIT4, Enum16.BIT8));
    mv32.toggle(MaskValue.with(Enum32.BIT4, Enum32.BIT8));
    mv40.toggle(MaskValue.with(Enum40.BIT4, Enum40.BIT8));
    mv64.toggle(MaskValue.with(Enum64.BIT4, Enum64.BIT8));
    mv80.toggle(MaskValue.with(Enum80.BIT4, Enum80.BIT8));

    for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", (e == Enum16.BIT2) || (e == Enum16.BIT8), mv16.isSet(e));
    for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", (e == Enum32.BIT2) || (e == Enum32.BIT8), mv32.isSet(e));
    for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", (e == Enum40.BIT2) || (e == Enum40.BIT8), mv40.isSet(e));
    for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", (e == Enum64.BIT2) || (e == Enum64.BIT8), mv64.isSet(e));
    for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", (e == Enum80.BIT2) || (e == Enum80.BIT8), mv80.isSet(e));
  }

  public void testSetValue () {
    // ==== SET WITH MASKVALUE ====
    MaskValue<Enum16> mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    MaskValue<Enum16> on16  = MaskValue.constant(Enum16.BIT1, Enum16.BIT3);
    MaskValue<Enum32> on32  = MaskValue.constant(Enum32.BIT1, Enum32.BIT3);
    MaskValue<Enum40> on40  = MaskValue.constant(Enum40.BIT1, Enum40.BIT3);
    MaskValue<Enum64> on64  = MaskValue.constant(Enum64.BIT1, Enum64.BIT3);
    MaskValue<Enum80> on80  = MaskValue.constant(Enum80.BIT1, Enum80.BIT3);

    MaskValue<Enum16> off16 = MaskValue.constant(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> off32 = MaskValue.constant(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> off40 = MaskValue.constant(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> off64 = MaskValue.constant(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> off80 = MaskValue.constant(Enum80.BIT2, Enum80.BIT4);

    mv16.setValue(on16);
    mv32.setValue(on32);
    mv40.setValue(on40);
    mv64.setValue(on64);
    mv80.setValue(on80);

    for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", (e == Enum16.BIT1) || (e == Enum16.BIT3), mv16.isSet(e));
    for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", (e == Enum32.BIT1) || (e == Enum32.BIT3), mv32.isSet(e));
    for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", (e == Enum40.BIT1) || (e == Enum40.BIT3), mv40.isSet(e));
    for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", (e == Enum64.BIT1) || (e == Enum64.BIT3), mv64.isSet(e));
    for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", (e == Enum80.BIT1) || (e == Enum80.BIT3), mv80.isSet(e));


    // ==== SET WITH ON/OFF WITH ENUM & STRING ====
    mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    mv16.setValue(Enum16.BIT1, true);   mv16.setValue("BIT3", true);
    mv32.setValue(Enum32.BIT1, true);   mv32.setValue("BIT3", true);
    mv40.setValue(Enum40.BIT1, true);   mv40.setValue("BIT3", true);
    mv64.setValue(Enum64.BIT1, true);   mv64.setValue("BIT3", true);
    mv80.setValue(Enum80.BIT1, true);   mv80.setValue("BIT3", true);

    mv16.setValue(Enum16.BIT2, false);  mv16.setValue("BIT4", false);
    mv32.setValue(Enum32.BIT2, false);  mv32.setValue("BIT4", false);
    mv40.setValue(Enum40.BIT2, false);  mv40.setValue("BIT4", false);
    mv64.setValue(Enum64.BIT2, false);  mv64.setValue("BIT4", false);
    mv80.setValue(Enum80.BIT2, false);  mv80.setValue("BIT4", false);

    for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", (e == Enum16.BIT1) || (e == Enum16.BIT3), mv16.isSet(e));
    for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", (e == Enum32.BIT1) || (e == Enum32.BIT3), mv32.isSet(e));
    for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", (e == Enum40.BIT1) || (e == Enum40.BIT3), mv40.isSet(e));
    for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", (e == Enum64.BIT1) || (e == Enum64.BIT3), mv64.isSet(e));
    for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", (e == Enum80.BIT1) || (e == Enum80.BIT3), mv80.isSet(e));

    // ==== SET WITH ON/OFF WITH MASKVALUE ====
    mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    mv16.setValue(on16, true);    mv16.setValue(off16, false);
    mv32.setValue(on32, true);    mv32.setValue(off32, false);
    mv40.setValue(on40, true);    mv40.setValue(off40, false);
    mv64.setValue(on64, true);    mv64.setValue(off64, false);
    mv80.setValue(on80, true);    mv80.setValue(off80, false);

    for (Enum16 e : Enum16.values()) assertEquals("mv16.isSet("+e+")", (e == Enum16.BIT1) || (e == Enum16.BIT3), mv16.isSet(e));
    for (Enum32 e : Enum32.values()) assertEquals("mv32.isSet("+e+")", (e == Enum32.BIT1) || (e == Enum32.BIT3), mv32.isSet(e));
    for (Enum40 e : Enum40.values()) assertEquals("mv40.isSet("+e+")", (e == Enum40.BIT1) || (e == Enum40.BIT3), mv40.isSet(e));
    for (Enum64 e : Enum64.values()) assertEquals("mv64.isSet("+e+")", (e == Enum64.BIT1) || (e == Enum64.BIT3), mv64.isSet(e));
    for (Enum80 e : Enum80.values()) assertEquals("mv80.isSet("+e+")", (e == Enum80.BIT1) || (e == Enum80.BIT3), mv80.isSet(e));

    // ======== MASK STRING ========
    //                           input                    output (null = All Set)
    final String[][] TESTS = { { null,                    "|BIT1|BIT3|"      }, // null
                               { "DEFAULT",               "|BIT1|BIT3|"      }, // only keyword
                               { "DEF",                   "|BIT1|BIT3|"      }, // only keyword
                               { "ALL",                   null               }, // only keyword
                               { "NONE",                  "|"                }, // only keyword
                               { "",                      "|"                }, // only keyword
                               { "DEFAULT|+BIT4|-BIT3",   "|BIT1|BIT4|"      }, // keyword then flags
                               { "DEFAULT|~BIT4|~BIT3",   "|BIT1|BIT4|"      }, // keyword then flags
                               { "DEF|+BIT4|-BIT3",       "|BIT1|BIT4|"      }, // keyword then flags
                               { "NONE|+BIT4|-BIT3",      "|BIT4|"           }, // keyword then flags
                               { "|+BIT4|-BIT3",          "|BIT4|"           }, // keyword then flags
                               { "+BIT5|-BIT2",           "|BIT4|BIT5|"      }, // flags (using current)
                               { "|+BIT5|-BIT2|",         "|BIT5|"           }, // flags (not using current)
                               { "|BIT5|BIT2|",           "|BIT2|BIT5|"      }, // flags (not using current)
                               { "BIT5|BIT2",             "|BIT2|BIT5|"      }, // flags (not using current)
                               { "BIT8",                  "|BIT8|"           }, // set
                               { "+BIT8",                 "|BIT2|BIT4|BIT8|" }, // add
                               { "-BIT4",                 "|BIT2|"           }, // remove
                               { "~BIT4",                 "|BIT2|"           }, // toggle (on->off)
                               { "~BIT8",                 "|BIT2|BIT4|BIT8|" }, // toggle (off->on)
                             };

    for (String[] test : TESTS) {
      String in  = test[0];
      String out = test[1]; // null -> All Set

      mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);  mv16.setValue(in, on16);
      mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);  mv32.setValue(in, on32);
      mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);  mv40.setValue(in, on40);
      mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);  mv64.setValue(in, on64);
      mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);  mv80.setValue(in, on80);

      if (out != null) {
        assertEquals("mv16.setValue("+in+")", out, mv16.toString());
        assertEquals("mv32.setValue("+in+")", out, mv32.toString());
        assertEquals("mv40.setValue("+in+")", out, mv40.toString());
        assertEquals("mv64.setValue("+in+")", out, mv64.toString());
        assertEquals("mv80.setValue("+in+")", out, mv80.toString());
      }
      else {
        for (Enum16 e : Enum16.values()) assertTrue("mv80.setValue("+in+")", mv16.isSet(e));
        for (Enum32 e : Enum32.values()) assertTrue("mv80.setValue("+in+")", mv32.isSet(e));
        for (Enum40 e : Enum40.values()) assertTrue("mv80.setValue("+in+")", mv40.isSet(e));
        for (Enum64 e : Enum64.values()) assertTrue("mv80.setValue("+in+")", mv64.isSet(e));
        for (Enum80 e : Enum80.values()) assertTrue("mv80.setValue("+in+")", mv80.isSet(e));
      }

      // verify that toString()->setValue(..) gets identical results
      MaskValue<Enum16> temp16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);  temp16.setValue(mv16.toString(), on16);
      MaskValue<Enum32> temp32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);  temp32.setValue(mv32.toString(), on32);
      MaskValue<Enum40> temp40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);  temp40.setValue(mv40.toString(), on40);
      MaskValue<Enum64> temp64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);  temp64.setValue(mv64.toString(), on64);
      MaskValue<Enum80> temp80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);  temp80.setValue(mv80.toString(), on80);

      assertEquals("temp16.setValue(mv16.toString(), ...)", mv16, temp16);
      assertEquals("temp32.setValue(mv32.toString(), ...)", mv32, temp32);
      assertEquals("temp40.setValue(mv40.toString(), ...)", mv40, temp40);
      assertEquals("temp64.setValue(mv64.toString(), ...)", mv64, temp64);
      assertEquals("temp80.setValue(mv80.toString(), ...)", mv80, temp80);

      assertEquals("fromString(mv16.toString(), ...)", mv16, MaskValue.fromString(mv16.toString(), Enum16.class));
      assertEquals("fromString(mv32.toString(), ...)", mv32, MaskValue.fromString(mv32.toString(), Enum32.class));
      assertEquals("fromString(mv40.toString(), ...)", mv40, MaskValue.fromString(mv40.toString(), Enum40.class));
      assertEquals("fromString(mv64.toString(), ...)", mv64, MaskValue.fromString(mv64.toString(), Enum64.class));
      assertEquals("fromString(mv80.toString(), ...)", mv80, MaskValue.fromString(mv80.toString(), Enum80.class));
    }
  }

  public void testFromString () { } // tested in testSetValue()

  public void testSetAll () {
    MaskValue<Enum16> mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    mv16.setAll();
    mv32.setAll();
    mv40.setAll();
    mv64.setAll();
    mv80.setAll();

    for (Enum16 e : Enum16.values()) assertTrue("mv16.isSet("+e+")", mv16.isSet(e));
    for (Enum32 e : Enum32.values()) assertTrue("mv32.isSet("+e+")", mv32.isSet(e));
    for (Enum40 e : Enum40.values()) assertTrue("mv40.isSet("+e+")", mv40.isSet(e));
    for (Enum64 e : Enum64.values()) assertTrue("mv64.isSet("+e+")", mv64.isSet(e));
    for (Enum80 e : Enum80.values()) assertTrue("mv80.isSet("+e+")", mv80.isSet(e));
  }

  public void testSetNone () {
    MaskValue<Enum16> mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    mv16.setNone();
    mv32.setNone();
    mv40.setNone();
    mv64.setNone();
    mv80.setNone();

    for (Enum16 e : Enum16.values()) assertFalse("mv16.isSet("+e+")", mv16.isSet(e));
    for (Enum32 e : Enum32.values()) assertFalse("mv32.isSet("+e+")", mv32.isSet(e));
    for (Enum40 e : Enum40.values()) assertFalse("mv40.isSet("+e+")", mv40.isSet(e));
    for (Enum64 e : Enum64.values()) assertFalse("mv64.isSet("+e+")", mv64.isSet(e));
    for (Enum80 e : Enum80.values()) assertFalse("mv80.isSet("+e+")", mv80.isSet(e));
  }

  public void testSetHexValue () {
    MaskValue<Enum16> mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    mv16.setHexValue(0x1|0x8);
    mv32.setHexValue(0x1|0x8);

    assertEquals("mv16.getHexValue()", 0x1|0x8, mv16.getHexValue());
    assertEquals("mv32.getHexValue()", 0x1|0x8, mv32.getHexValue());
    assertEquals("mv16.getHexValue()", "|BIT1|BIT4|", mv16.toString());
    assertEquals("mv32.getHexValue()", "|BIT1|BIT4|", mv32.toString());

    Exception e40 = null;
    Exception e64 = null;
    Exception e80 = null;

    try { mv40.setHexValue(0x8|0x12); } catch (Exception e) { e40 = e; }
    try { mv64.setHexValue(0x8|0x12); } catch (Exception e) { e64 = e; }
    try { mv80.setHexValue(0x8|0x12); } catch (Exception e) { e80 = e; }

    assertNotNull("Caught exception for mv40.setHexValue(0x8|0x12)", e40);
    assertNotNull("Caught exception for mv64.setHexValue(0x8|0x12)", e64);
    assertNotNull("Caught exception for mv80.setHexValue(0x8|0x12)", e80);
  }

  public void testGetHexValue () {
    // ==== BASIC GET ====
    MaskValue<Enum16> mv16 = MaskValue.with(Enum16.BIT2, Enum16.BIT4);
    MaskValue<Enum32> mv32 = MaskValue.with(Enum32.BIT2, Enum32.BIT4);
    MaskValue<Enum40> mv40 = MaskValue.with(Enum40.BIT2, Enum40.BIT4);
    MaskValue<Enum64> mv64 = MaskValue.with(Enum64.BIT2, Enum64.BIT4);
    MaskValue<Enum80> mv80 = MaskValue.with(Enum80.BIT2, Enum80.BIT4);

    assertEquals("mv16.getHexValue()", 0x2|0x8, mv16.getHexValue());
    assertEquals("mv32.getHexValue()", 0x2|0x8, mv32.getHexValue());

    Exception e40 = null;
    Exception e64 = null;
    Exception e80 = null;

    try { mv40.getHexValue(); } catch (Exception e) { e40 = e; }
    try { mv64.getHexValue(); } catch (Exception e) { e64 = e; }
    try { mv80.getHexValue(); } catch (Exception e) { e80 = e; }

    assertNotNull("Caught exception for mv40.getHexValue()", e40);
    assertNotNull("Caught exception for mv64.getHexValue()", e64);
    assertNotNull("Caught exception for mv80.getHexValue()", e80);

    // ==== STATIC GET ====
    for (int i = 0; i < 16; i++) {
      Enum16 e = Enum16.valueOf("BIT"+(i+1));
      assertEquals("MaskValue.getHexValue(Enum16."+e+")", 1<<i, MaskValue.getHexValue(e));
    }
    for (int i = 0; i < 32; i++) {
      Enum32 e = Enum32.valueOf("BIT"+(i+1));
      assertEquals("MaskValue.getHexValue(Enum32."+e+")", 1<<i, MaskValue.getHexValue(e));
    }

    e40 = null;
    e64 = null;
    e80 = null;

    try { MaskValue.getHexValue(Enum40.BIT1); } catch (Exception e) { e40 = e; }
    try { MaskValue.getHexValue(Enum64.BIT1); } catch (Exception e) { e64 = e; }
    try { MaskValue.getHexValue(Enum80.BIT1); } catch (Exception e) { e80 = e; }

    assertNotNull("Caught exception for MaskValue.getHexValue(Enum40.BIT1)", e40);
    assertNotNull("Caught exception for MaskValue.getHexValue(Enum64.BIT1)", e64);
    assertNotNull("Caught exception for MaskValue.getHexValue(Enum80.BIT1)", e80);
  }
}
