/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2006, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package test.compliance.objectname;  
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.framework.Test;

import javax.management.MalformedObjectNameException; 
import javax.management.ObjectName; 
import java.util.Hashtable;  

/**
 * Hammer ObjectName, making sure it spots all malformed inputs.
 *
 * This may look like overkill but it's not.  I want each
 * permutation to run independantly for full test coverage.
 *
 * This suite has twice as many tests (about 60) as my last
 * testcase - and for that it caught one extra bug for me.
 *
 * @todo check we are throwing MalformedObjectNameException and NullPointerException
 *       for the right errors
 * @author  <a href="mailto:trevor@protocool.com">Trevor Squires</a>.  
 */ 
public class MalformedSUITE extends TestSuite {
   public static final String GOOD_DOMAIN = "domain";
   public static final String GOOD_KEY = "key1";
   public static final String GOOD_VALUE = "val1";

   // strings containing illegal chars to use in keys
   public static final String[] BAD_KEYS = {
      "",            // cannot be zero sized
      "som:thing",   // cannot contain domain separator
      "som?thing",   // cannot contain pattern chars
      "som*thing",   // cannot contain pattern chars
      "som,thing",   // cannot contain kvp chunk separator
      "som=thing",   // cannot contain kvp separator
      "som\nthing",   // cannot contain \n separator
   };

   // strings containing illegal chars to use in values
   public static final String[] BAD_VALS = {
      "som:thing",   // cannot contain domain separator
      "som?thing",   // cannot contain pattern chars
      "som*thing",   // cannot contain pattern chars
      "som,thing",   // cannot contain kvp chunk separator
      "som=thing",   // cannot contain kvp separator
      "som\nthing",   // cannot contain \n separator
      "som\"thing",   // unterminated quote
      "som\"\\b\"thing",   // bad escape inside quote
      "som\"" + '\n' + "\"thing",   // new line inside quote
      "som\"\"\"thing",   // quote inside quote
      "som\"?\"thing",   // question mark inside quote
      "som\"*\"thing",   // asterisk inside quote
   };

   // domains containing illegal domain chars
   public static final String[] BAD_DOMAINS = {
      "doma:in",    // : char in domain
      "doma\nin",    // \n char in domain
   };

   // pre-cooked name strings dealing with structural malformations    
   public static final String[] BAD_FULLNAMES = {
      "domain:key=val,key=val2",    // duplicate key
      "domain:=,foo=bar",           // both key and value empty
      "domain:key=val,,foo=bar",    // missing kvp in middle
      "domain:,key=val,foo=bar",    // missing kvp at beginning
      "domain:key=val,foo=bar,",    // missing kvp at end
      "domain:key=val,   ,foo=bar", // malformed kvp, no = char
      "domain:key=val,*,*", // duplicate asterisks
      "domain:*,key=val,*", // duplicate asterisks
      "domain:*,key1=val1,*,key2=val2", // duplicate asterisks
      "domain: *,key1=val1,key2=val2", // asterisk with a leading space
      "domain:key1=val1,key2=val2, *", // asterisk with a leading space
      "domain:key1=val1,key2=val2,* ", // asterisk with a trailing space
      "domain:", // no properties
      null,                         // null is not allowed
   };

   public static void main(String[] args)
   {
      junit.textui.TestRunner.run(suite());
   }

   public static Test suite()    {
      TestSuite suite = new TestSuite("All Malformed Tests"); 
      // Tests for nulls
      suite.addTest(new DomainKeyValueTEST(null, null, null));
      suite.addTest(new DomainKeyValueTEST(null, "key1", "val1"));
      suite.addTest(new DomainKeyValueTEST("domain", null, "val1"));
      suite.addTest(new DomainKeyValueTEST("domain", "key1", null));
      suite.addTest(new DomainKeyValueTEST("domain", null, null));
      suite.addTest(new DomainHashtableTEST(null, "key1", "val1"));

      // extra stuff related to null or zero sized hashtable
      suite.addTestSuite(DomainHashtableExtraTEST.class);

      // all illegal domain characters
      for (int i = 0; i < BAD_DOMAINS.length; i++)
      {
         suite.addTest(new FullNameTEST(BAD_DOMAINS[i] + ":" + GOOD_KEY + "=" + GOOD_VALUE));
         suite.addTest(new DomainKeyValueTEST(BAD_DOMAINS[i], GOOD_KEY, GOOD_VALUE));
         suite.addTest(new DomainHashtableTEST(BAD_DOMAINS[i], GOOD_KEY, GOOD_VALUE));
      }

      // all illegal key characters
      for (int i = 0; i < BAD_KEYS.length; i++)
      {
         suite.addTest(new FullNameTEST(GOOD_DOMAIN + ":" + BAD_KEYS[i] + "=" + GOOD_VALUE));
         suite.addTest(new DomainKeyValueTEST(GOOD_DOMAIN, BAD_KEYS[i], GOOD_VALUE));
         suite.addTest(new DomainHashtableTEST(GOOD_DOMAIN, BAD_KEYS[i], GOOD_VALUE));
      }

      // all illegal value characters
      for (int i = 0; i < BAD_VALS.length; i++)
      {
         suite.addTest(new FullNameTEST(GOOD_DOMAIN + ":" + GOOD_KEY + "=" + BAD_VALS[i]));
         suite.addTest(new DomainKeyValueTEST(GOOD_DOMAIN, GOOD_KEY, BAD_VALS[i]));
         suite.addTest(new DomainHashtableTEST(GOOD_DOMAIN, GOOD_KEY, BAD_VALS[i]));
      }

      // all the structurally malformed fullnames
      for (int i = 0; i < BAD_FULLNAMES.length; i++)
      {
         suite.addTest( new FullNameTEST(BAD_FULLNAMES[i]));
      }

      return suite;
   } 

   public static class FullNameTEST extends TestCase
   {
      private String fullName;

      public FullNameTEST(String fullName)
      {
         super("testMalformed");
         this.fullName = fullName;
      }

      public void testMalformed()
      {
         boolean caught = false;
         try
         {
            ObjectName name = new ObjectName(fullName);
         }
         catch (MalformedObjectNameException e)
         {
            caught = true;
         }
         catch (NullPointerException e)
         {
            caught = true;
            if (fullName != null)
               fail("Unexpected NullPointerException for " + fullName);
         }
         if (caught == false)
         {
            if (fullName != null)
               if (fullName.equals("domain:=val1") ||
                   fullName.equals("domain:=,foo=bar"))
                  fail("FAILS IN RI: expected a MalformedObjectNameException for: " + fullName);
            fail("expected a MalformedObjectNameException for: " + fullName);
         }

         caught = false;
         try
         {
            ObjectName name = ObjectName.getInstance(fullName);
         }
         catch (MalformedObjectNameException e)
         {
            caught = true;
         }
         catch (NullPointerException e)
         {
            caught = true;
            if (fullName != null)
               fail("Unexpected NullPointerException for " + fullName);
         }
         if (caught == false)
         {
            if (fullName != null)
               if (fullName.equals("domain:=val1") ||
                   fullName.equals("domain:=,foo=bar"))
                  fail("FAILS IN RI: expected a MalformedObjectNameException for: " + fullName);
            fail("expected a MalformedObjectNameException for: " + fullName);
         }
      }
   }

   public static class DomainKeyValueTEST extends TestCase
   {
      private String domain;
      private String key;
      private String value;

      public DomainKeyValueTEST(String domain, String key, String value)
      {
         super("testMalformed");
         this.domain = domain;
         this.key = key;
         this.value = value;
      }

      public void testMalformed()
      {
         boolean caught = false;
         try
         {
            ObjectName name = new ObjectName(domain, key, value);
         }
         catch (MalformedObjectNameException e)
         {
            caught = true;
         }
         catch (NullPointerException e)
         {
            caught = true;
            if (domain != null && key != null && value != null)
               fail("Unexpected NullPointerException for " + domain + ":" + key + "=" + value);
         }
         if (caught == false)
         {
            if (value != null)
               if (value.equals("som\"thing") ||
                   value.equals("som\"\\b\"thing") ||
                   value.equals("som\"\"\"thing"))
                  fail("FAILS IN RI: expected a MalformedObjectNameException for: " + domain + ":" + key + "=" + value);
            fail("expected a MalformedObjectNameException for: " + domain + ":" + key + "=" + value);
         }

         caught = false;
         try
         {
            ObjectName name = ObjectName.getInstance(domain, key, value);
         }
         catch (MalformedObjectNameException e)
         {
            caught = true;
         }
         catch (NullPointerException e)
         {
            caught = true;
            if (domain != null && key != null && value != null)
               fail("Unexpected NullPointerException for " + domain + ":" + key + "=" + value);
         }
         if (caught == false)
         {
            if (value != null)
               if (value.equals("som\"thing") ||
                   value.equals("som\"\\b\"thing") ||
                   value.equals("som\"\"\"thing"))
                  fail("FAILS IN RI: expected a MalformedObjectNameException for: " + domain + ":" + key + "=" + value);
            fail("expected a MalformedObjectNameException for: " + domain + ":" + key + "=" + value);
         }
      }
   }

   public static class DomainHashtableTEST extends TestCase
   {
      private String domain;
      private String key;
      private String value;

      public DomainHashtableTEST(String domain, String key, String value)
      {
         super("testMalformed");
         this.domain = domain;
         this.key = key;
         this.value = value;
      }

      public void testMalformed()
      {
         boolean caught = false;
         try
         {
            Hashtable h = new Hashtable();
            h.put(key, value);
            ObjectName name = new ObjectName(domain, h);
         }
         catch (MalformedObjectNameException e)
         {
            caught = true;
         }
         catch (NullPointerException e)
         {
            caught = true;
            if (domain != null && key != null && value != null)
               fail("Unexpected NullPointerException for " + domain + ":" + key + "=" + value);
         }
         if (caught == false)
         {
            if (value != null)
               if (value.equals("som\"thing") ||
                   value.equals("som\"\\b\"thing") ||
                   value.equals("som\"\"\"thing"))
                  fail("FAILS IN RI: expected a MalformedObjectNameException for: " + domain + ":" + key + "=" + value);
            fail("expected a MalformedObjectNameException for: " + domain + ":" + key + "=" + value);
         }

         caught = false;
         try
         {
            Hashtable h = new Hashtable();
            h.put(key, value);
            ObjectName name = ObjectName.getInstance(domain, h);
         }
         catch (MalformedObjectNameException e)
         {
            caught = true;
         }
         catch (NullPointerException e)
         {
            caught = true;
            if (domain != null && key != null && value != null)
               fail("Unexpected NullPointerException for " + domain + ":" + key + "=" + value);
         }
         if (caught == false)
         {
            if (value != null)
               if (value.equals("som\"thing") ||
                   value.equals("som\"\\b\"thing") ||
                   value.equals("som\"\"\"thing"))
                  fail("FAILS IN RI: expected a MalformedObjectNameException for: " + domain + ":" + key + "=" + value);
            fail("expected a MalformedObjectNameException for: " + domain + ":" + key + "=" + value);
         }
      }
   }

   public static class DomainHashtableExtraTEST extends TestCase
   {
      public DomainHashtableExtraTEST(String s)
      {
         super(s);
      }

      public void testNullDomain()
      {
         Hashtable h = new Hashtable();
         h.put(new Object(), GOOD_VALUE);
         doCheck(null, h, "<null domain>", true);
      }

      public void testNullHashtable()
      {
         doCheck(GOOD_DOMAIN, null, "<null hashtable>", true);
      }

      public void testEmptyHashtable()
      {
         doCheck(GOOD_DOMAIN, new Hashtable(), "<empty_hashtable>", false);
      }

      public void testNonStringKey()
      {
         Hashtable h = new Hashtable();
         h.put(new Object(), GOOD_VALUE);
         doCheck(GOOD_DOMAIN, h, "<non_string_key>=" + GOOD_VALUE, false);
      }

      public void testNonStringValue()
      {
         Hashtable h = new Hashtable();
         h.put(GOOD_KEY, new Object());
         doCheck(GOOD_DOMAIN, h, GOOD_KEY + "=<non_string_value>", false);
      }

      private void doCheck(String domain, Hashtable h, String failureHint, boolean expectNull)
      {
         boolean caught = true;
         try
         {
            ObjectName name = new ObjectName(domain, h);
         }
         catch (MalformedObjectNameException e)
         {
            caught = true;
            if (expectNull)
               fail("FAILS IN RI: Expected a NullPointerException for: " + domain + ":" + failureHint);
         }
         catch (NullPointerException e)
         {
            if (expectNull == false)
               fail("unexpected a NullPointerException for: " + domain + ":" + failureHint);
         }
         if (caught == false)
            fail("expected a MalformedObjectNameException for: " + domain + ":" + failureHint);

         caught = true;
         try
         {
            ObjectName name = ObjectName.getInstance(domain, h);
         }
         catch (MalformedObjectNameException e)
         {
            caught = true;
            if (expectNull)
               fail("FAILS IN RI: Expected a NullPointerException for: " + domain + ":" + failureHint);
         }
         catch (NullPointerException e)
         {
            if (expectNull == false)
               fail("unexpected a NullPointerException for: " + domain + ":" + failureHint);
         }
         if (caught == false)
            fail("expected a MalformedObjectNameException for: " + domain + ":" + failureHint);
      }
   }
} 