/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2009-2010 Sun Microsystems, Inc. * Portions Copyright 2010 ForgeRock AS. */ package org.opends.server.schema; import java.util.Calendar; import java.util.GregorianCalendar; import static org.testng.Assert.*; import java.util.List; import java.util.TimeZone; import org.opends.server.TestCaseUtils; import org.opends.server.api.MatchingRule; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.protocols.ldap.LDAPFilter; import org.opends.server.types.ByteString; import org.opends.server.types.ConditionResult; import org.opends.server.types.DN; import org.opends.server.types.DereferencePolicy; import org.opends.server.types.DirectoryException; import org.opends.server.types.ResultCode; import org.opends.server.types.SearchResultEntry; import org.opends.server.types.SearchScope; import org.opends.server.util.TimeThread; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.opends.server.schema.GeneralizedTimeSyntax.*; import static org.opends.server.schema.SchemaConstants.*; /** * This class tests various time-based matching rules. */ public final class TimeBasedMatchingRuleTest extends SchemaTestCase { //User DNs to be used in tests. private DN user1; private DN user2 ; private DN user3; private DN user4; private DN user5; private DN user6; private DN user7; private DN user8; private final static String TIME_ATTR = "test-time"; private final static String DATE_ATTR = "test-date"; /** * Ensures that the Directory Server is running before executing the * testcases. * * @throws Exception If an unexpected problem occurs. */ @BeforeClass() public void startServer() throws Exception { TestCaseUtils.startServer(); TestCaseUtils.initializeTestBackend(true); user1 = DN.decode("cn=user1,dc=example,dc=com"); user2 = DN.decode("cn=user2,dc=example,dc=com"); user3 = DN.decode("cn=user3,dc=example,dc=com"); user4 = DN.decode("cn=user4,dc=example,dc=com"); user5 = DN.decode("cn=user5,dc=example,dc=com"); user6 = DN.decode("cn=user6,dc=example,dc=com"); user7 = DN.decode("cn=user7,dc=example,dc=com"); user8 = DN.decode("cn=user!,dc=example,dc=com"); /** Extend the schema and add an attribute which is baseed on generalizedTimeSyntax. Since all the existing attributes based on that syntax are read-only, let us create a new attribute and add it.*/ int resultCode = TestCaseUtils.applyModifications(true, "dn: cn=schema", "changetype: modify", "add: attributeTypes", "attributeTypes: ( test-time-oid NAME 'test-time' DESC 'Test time attribute' EQUALITY " + "generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )", "attributeTypes: ( test-date-oid NAME 'test-date' DESC 'Test date attribute' EQUALITY " + "generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )", "-", "add: objectclasses", "objectclasses: ( testoc-oid NAME 'testOC' SUP top AUXILIARY MUST test-time)", "objectclasses: ( testoc2-oid NAME 'testOC2' SUP top AUXILIARY MUST test-date)" ); assertTrue(resultCode == 0); } /** * Test to search using the less-than relative time matching rule for expired events. */ @Test() public void testRTLessThanExpiredEvents() throws Exception { try { populateEntries(); InternalClientConnection conn = InternalClientConnection.getRootConnection(); InternalSearchOperation searchOperation = new InternalSearchOperation( conn, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, ByteString.valueOf("dc=example,dc=com"), SearchScope.WHOLE_SUBTREE, DereferencePolicy.NEVER_DEREF_ALIASES, Integer.MAX_VALUE, Integer.MAX_VALUE, false, LDAPFilter.decode(TIME_ATTR+":"+EXT_OMR_RELATIVE_TIME_LT_OID+":=-60m"), // null, null); searchOperation.run(); assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); List<SearchResultEntry> entries = searchOperation.getSearchEntries(); assertTrue(dnFoundInEntryList(entries,user1,user2)); } finally { TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); } } /** * Test to search using the less-than relative time matching rule for future events. */ @Test() public void testRTLessThanFutureEvents() throws Exception { try { populateEntries(); InternalClientConnection conn = InternalClientConnection.getRootConnection(); InternalSearchOperation searchOperation = new InternalSearchOperation( conn, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, ByteString.valueOf("dc=example,dc=com"), SearchScope.WHOLE_SUBTREE, DereferencePolicy.NEVER_DEREF_ALIASES, Integer.MAX_VALUE, Integer.MAX_VALUE, false, LDAPFilter.decode(TIME_ATTR+":"+EXT_OMR_RELATIVE_TIME_LT_OID+":=1d"), null, null); searchOperation.run(); assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); List<SearchResultEntry> entries = searchOperation.getSearchEntries(); assertTrue(entries.size() == 4 && dnFoundInEntryList(entries,user1,user2,user3,user5)); } finally { TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); } } /** * Test to search using the greater-than relative time matching rule for expired events. */ @Test() public void testRTGreaterThanExpiredEvents() throws Exception { try { populateEntries(); InternalClientConnection conn = InternalClientConnection.getRootConnection(); InternalSearchOperation searchOperation = new InternalSearchOperation( conn, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, ByteString.valueOf("dc=example,dc=com"), SearchScope.WHOLE_SUBTREE, DereferencePolicy.NEVER_DEREF_ALIASES, Integer.MAX_VALUE, Integer.MAX_VALUE, false, LDAPFilter.decode(TIME_ATTR+":"+EXT_OMR_RELATIVE_TIME_GT_OID+":=-1h"), null, null); searchOperation.run(); assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); List<SearchResultEntry> entries = searchOperation.getSearchEntries(); assertTrue(entries.size()==3 && dnFoundInEntryList(entries,user3,user4,user5)); } finally { TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); } } /** * Test to search using the greater-than relative time matching rule for future events. */ @Test() public void testRTGreaterThanFutureEvents() throws Exception { try { populateEntries(); InternalClientConnection conn = InternalClientConnection.getRootConnection(); InternalSearchOperation searchOperation = new InternalSearchOperation( conn, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, ByteString.valueOf("dc=example,dc=com"), SearchScope.WHOLE_SUBTREE, DereferencePolicy.NEVER_DEREF_ALIASES, Integer.MAX_VALUE, Integer.MAX_VALUE, false, LDAPFilter.decode(TIME_ATTR+":"+EXT_OMR_RELATIVE_TIME_GT_OID+":=0s"), null, null); searchOperation.run(); assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); List<SearchResultEntry> entries = searchOperation.getSearchEntries(); assertTrue(entries.size()==2 && dnFoundInEntryList(entries,user3,user4)); } finally { TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); } } /** * Test to search using the partial date and time matching rule * for an assertion value. * Dates for this test are hardcoded to avoid test failures depending * on when the tests are launched. */ @Test() public void testPartialDateNTimeMatchingRuleUsingSearch() throws Exception { try { populateEntries(); InternalClientConnection conn = InternalClientConnection.getRootConnection(); String assertion = "01D11M"; InternalSearchOperation searchOperation = new InternalSearchOperation( conn, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, ByteString.valueOf("dc=example,dc=com"), SearchScope.WHOLE_SUBTREE, DereferencePolicy.NEVER_DEREF_ALIASES, Integer.MAX_VALUE, Integer.MAX_VALUE, false, LDAPFilter.decode(DATE_ATTR+":"+EXT_PARTIAL_DATE_TIME_OID+":="+assertion), null,null); searchOperation.run(); assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); List<SearchResultEntry> entries = searchOperation.getSearchEntries(); assertTrue(entries.size()==1 && dnFoundInEntryList(entries,user6)); } finally { TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); } } /** * Test to match the attribute and the assertion values using a partial date and time * matching rule. */ @Test(dataProvider="partialDateTimeValues") public void testPartialDateNTimeMatch(long attributeValue,String assertionValue) throws Exception { MatchingRule partialTimeRule = DirectoryServer.getMatchingRule( EXT_PARTIAL_DATE_TIME_NAME.toLowerCase()); ByteString str = partialTimeRule.normalizeAssertionValue(ByteString.valueOf(assertionValue)); assertTrue(partialTimeRule.valuesMatch(ByteString.valueOf(attributeValue), str) == ConditionResult.TRUE); } /** * Tests the assertion syntax of the relative time matching rules. */ @Test(dataProvider= "relativeTimeValues") public void testRelativeTimeMatchingRuleAssertionSyntax(String assertion,boolean isValid) { MatchingRule relativeTimeLTRule = DirectoryServer.getOrderingMatchingRule( EXT_OMR_RELATIVE_TIME_LT_ALT_NAME.toLowerCase()); boolean exception = false; try { relativeTimeLTRule.normalizeAssertionValue(ByteString.valueOf(assertion)); } catch(DirectoryException e) { //invalid values will throw an exception. exception = true; assertTrue(!isValid); } if(!isValid) { //An invalid value can't get away without throwing exception. assertTrue(exception); } } /** * Tests the assertion syntax of the partial date and time matching rules. */ @Test(dataProvider= "partialDateTimeSyntaxes") public void testPartialDateTimeMatchingRuleAssertionSyntax(String assertion,boolean isValid) { MatchingRule partialDTRule = DirectoryServer.getMatchingRule(EXT_PARTIAL_DATE_TIME_OID); boolean exception = false; try { partialDTRule.normalizeAssertionValue(ByteString.valueOf(assertion)); } catch(DirectoryException e) { //invalid values will throw an exception. exception = true; assertTrue(!isValid); } if(!isValid) { assertTrue(exception); } } /** * Generates data for testing relative time matching rule assertion syntax. */ @DataProvider(name="relativeTimeValues") private Object[][] createRelativeTimeValues() { return new Object[][] { {"1s",true}, {"1s0d",false}, {"-1d",true}, {"2h",true}, {"+2w",true}, {"0",true}, {"0s",true}, {"0d",true}, {"xyz",false}, {"12w-2d",false}, {"1s2s",false}, {"1d4s5d",false} }; } /** * Generates the data for testing partial time date and time values. */ @DataProvider(name="partialDateTimeValues") private Object[][] createPartialDateTimeValues() { GregorianCalendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC")); c.setLenient(false); c.clear(); c.set(Calendar.HOUR_OF_DAY,23); c.set(Calendar.MINUTE,0); c.set(Calendar.SECOND,0); long time1 = c.getTimeInMillis(); c.set(Calendar.HOUR_OF_DAY,00); c.set(Calendar.MINUTE,59); c.set(Calendar.SECOND,59); long time2 = c.getTimeInMillis(); return new Object[][] { {time1,"0s"}, {time1,"0m"}, {time1,"23h"}, {time2,"59m59s"}, {time2,"0h59m59s"} }; } /** * Generates data for testing partial date and time assertion syntax. */ @DataProvider(name="partialDateTimeSyntaxes") private Object[][] createPartialDateTimeSyntaxes() { //Get the current time. GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); cal.setLenient(false); //Get the date today. int second = cal.get(Calendar.SECOND); int minute = cal.get(Calendar.MINUTE); int hour = cal.get(Calendar.HOUR); int date = cal.get(Calendar.DATE); int month = cal.get(Calendar.MONTH) + 1; int year = cal.get(Calendar.YEAR); return new Object[][] { {"20MM30DD1978YY",false}, {"02MM29DD2009YY",false}, {"02MM31DD2010YY",false}, {"-1s",false}, {"02M29D2008Y",true}, {"DDYY",false}, {"02D",true}, {"12M",true}, {"1978Y",true}, {"0MM",false}, {"20MM03DD10MM",false}, {"00s12m13h",true}, {"00s12m14h1M3D1978Y",true}, {"1s",true}, {"12m",true}, {"23h",true}, {"61s",false}, {"60m",false}, {"24h",false}, {second+"s",true}, {minute+"m",true}, {hour+"h",true}, {date+"D",true}, {month+"M",true}, {year+"Y",true}, {month+"M"+date+"D",true}, {year+"Y"+date+"D",true}, {month+"M"+year+"Y"+date+"D",true} }; } //validate if the args are found in the entries list. private boolean dnFoundInEntryList( List<SearchResultEntry> entries,DN ... dns) { for(DN dn: dns) { boolean found = false; for(SearchResultEntry entry: entries) { System.out.println("dn from the current entry is " + entry.getDN()); if(entry.getDN().equals(dn)) { found = true; } } if(!found) { return false; } } return true; } //Creates the entries. private void populateEntries() throws Exception { //Get the current time from the TimeThread. Using the current time from new // calendar may fail if the time thread using a stale time. long currentTime = TimeThread.getTime(); TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com"); TestCaseUtils.addEntries( "dn: cn=user1,dc=example,dc=com", "objectclass: person", "objectclass: testoc", "cn: user1", "sn: user1", TIME_ATTR + ": "+ format(currentTime-4000*1000), //more than 1 hour old. "", "dn: cn=user2,dc=example,dc=com", "objectclass: person", "objectclass: testoc", "cn: user2", "sn: user2", TIME_ATTR + ": " + format(currentTime-25*3600*1000), //more than a day old. "", "dn: cn=user3,dc=example,dc=com", "objectclass: person", "objectclass: testoc", "cn: user3", "sn: user3", TIME_ATTR + ": " + format(currentTime+4000*1000), //more than 1 hour in advance. "", "dn: cn=user4,dc=example,dc=com", "objectclass: person", "objectclass: testoc", "cn: user4", "sn: user4", TIME_ATTR + ": " + format(currentTime+25*3600*1000), // more than 1 day in advance "", "dn: cn=user5,dc=example,dc=com", "objectclass: person", "objectclass: testoc", "cn: user5", "sn: user5", TIME_ATTR + ": " + format(currentTime), // now. "", "dn: cn=user6,dc=example,dc=com", "objectclass: person", "objectclass: testoc2", "cn: user6", "sn: user6", DATE_ATTR + ": 19651101000000Z", // Nov 1st 1965 "", "dn: cn=user7,dc=example,dc=com", "objectclass: person", "objectclass: testoc2", "cn: user7", "sn: user7", DATE_ATTR + ": 20101104000000Z", // Nov 4th 2010 "", "dn: cn=user8,dc=example,dc=com", "objectclass: person", "objectclass: testoc2", "cn: user8", "sn: user8", DATE_ATTR + ": 20000101000000Z" // Jan 1st 2000 ); } }