/* * This file is part of the Heritrix web crawler (crawler.archive.org). * * Licensed to the Internet Archive (IA) by one or more individual * contributors. * * The IA licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.archive.util; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Pattern; import junit.framework.TestCase; /** * Tests (and * @author gojomo */ public class InterruptibleCharSequenceTest extends TestCase { // this regex takes many seconds to fail on the input // (~20 seconds on 2Ghz Athlon64 JDK 1.6) public static String BACKTRACKER = "^(((((a+)*)*)*)*)*$"; public static String INPUT = "aaaaab"; /** * Development-time benchmarking of InterruptibleCharSequence in * regex use. (Rename 'xest' to 'test' if wanted as unit test, * but never actually fails anything -- just measures.) * * For reference the regex "^(((((a+)*)*)*)*)*$" requires * 239,286,636 charAt(s) to fail on "aaaaab", which takes * around 20 seconds on a 2Ghz Athlon64(x2) with JDK 1.6. * The runtime overhead of checking interrupt status in this * extreme case is around 5% in my tests. */ public void xestOverhead() { String regex = BACKTRACKER; String inputNormal = INPUT; InterruptibleCharSequence inputWrapped = new InterruptibleCharSequence(inputNormal); // warm up tryMatch(inputNormal,regex); tryMatch(inputWrapped,regex); // inputWrapped.counter=0; int trials = 5; long stringTally = 0; long icsTally = 0; for(int i = 1; i <= trials; i++) { System.out.println("trial "+i+" of "+trials); long start = System.currentTimeMillis(); System.out.print("String "); tryMatch(inputNormal,regex); long end = System.currentTimeMillis(); System.out.println(end-start); stringTally += (end-start); start = System.currentTimeMillis(); System.out.print("InterruptibleCharSequence "); tryMatch(inputWrapped,regex); end = System.currentTimeMillis(); System.out.println(end-start); //System.out.println(inputWrapped.counter+" steps"); //inputWrapped.counter=0; icsTally += (end-start); } System.out.println("InterruptibleCharSequence took "+((float)icsTally)/stringTally+" longer."); } public boolean tryMatch(CharSequence input, String regex) { return Pattern.matches(regex,input); } public Thread tryMatchInThread(final CharSequence input, final String regex, final BlockingQueue<Object> atFinish) { Thread t = new Thread() { public void run() { boolean result; try { result = tryMatch(input,regex); } catch (Exception e) { atFinish.offer(e); return; } atFinish.offer(result); } }; t.start(); return t; } public void testNoninterruptible() throws InterruptedException { BlockingQueue<Object> q = new LinkedBlockingQueue<Object>(); Thread t = tryMatchInThread(INPUT, BACKTRACKER, q); Thread.sleep(1000); t.interrupt(); Object result = q.take(); assertTrue("mismatch uncompleted",Boolean.FALSE.equals(result)); } public void testInterruptibility() throws InterruptedException { BlockingQueue<Object> q = new LinkedBlockingQueue<Object>(); Thread t = tryMatchInThread(new InterruptibleCharSequence(INPUT), BACKTRACKER, q); Thread.sleep(500); t.interrupt(); Object result = q.take(); if(result instanceof Boolean) { System.err.println(result+" match beat interrupt"); } assertTrue("exception not thrown",result instanceof RuntimeException); } }