001// Copyright 2009 by Basil Vandegriend.  All rights reserved.
002
003package com.basilv.examples.concurrency;
004
005import static org.junit.Assert.*;
006
007import java.util.*;
008import java.util.concurrent.atomic.AtomicReference;
009
010import org.junit.*;
011
012public class DateProcessorMultiThreadedTest
013{
014  
015  @Before
016  public void setUp() {
017    DateProcessor.setInstanceForTesting(null);
018  }
019
020  @Test
021  public void multithreadedExecutionOneThread() {
022    verifyMultiThreadedExecution(1); // Should always pass
023  }
024
025  @Test
026  public void multithreadedExecutionFewThreads() {
027    verifyMultiThreadedExecution(3); // Always seems to pass
028  }
029  
030  @Test
031  public void multithreadedExecutionManyThreads() {
032    boolean successful = false;
033    try {
034      // 500 always seems to fail given my hardware/software platform, even when 
035      // entire Examples suite is executed. Smaller numbers inconsistently fail, especially
036      // when entire suite is executed.
037      verifyMultiThreadedExecution(500); 
038      successful = true;
039    } catch (AssertionError e) {
040      // Expected case.
041      successful = false;
042      System.err.print("Expected failure due to:" + e.getMessage());
043    }
044    if (successful) {
045      fail("Expected exception");
046    }
047  }
048
049  private void verifyMultiThreadedExecution(int totalExecutions) {
050    int totalValid = totalExecutions / 2;
051    List<DateProcessorThread> threads = new ArrayList<DateProcessorThread>(totalExecutions);
052
053    // Execute threads
054    for (int i = 0; i < totalValid; i++) {
055      DateProcessorThread thread = createAndStartThread("2009-11-25", true);
056      threads.add(thread);
057      
058    }
059    for (int i = 0; i < totalExecutions - totalValid; i++) {
060      DateProcessorThread thread = createAndStartThread("foo", false);
061      threads.add(thread);
062    }
063    
064    // Wait for all threads to be finished
065    for (Thread thread : threads) {
066      try {
067        thread.join();
068      } catch (InterruptedException e) {
069        // Ignore for testing.
070      }
071    }
072    
073    // Verify totals
074    assertEquals("Total execution count", totalExecutions, DateProcessor.getInstance().getDatesProcessedCount());
075    assertEquals("Total valid count", totalValid, DateProcessor.getInstance().getDatesValidCount());
076    
077    // Verify individual results
078    for (DateProcessorThread thread : threads) {
079      thread.verifyResults();
080    }
081  }
082
083  private DateProcessorThread createAndStartThread(String input, boolean expectValid) {
084    DateProcessorThread thread = new DateProcessorThread(input, expectValid);
085    thread.start();
086    return thread;
087  }
088  
089  private static class DateProcessorThread extends Thread {
090
091    private final String input;
092    private final boolean expectValid;
093    private AtomicReference<Date> resultRef = new AtomicReference<Date>();
094    
095    public DateProcessorThread(String input, boolean expectValid) {
096      this.input = input;
097      this.expectValid = expectValid;
098    }
099
100    @Override
101    public void run() {
102      Date date = DateProcessor.getInstance().processDate(input);
103      resultRef.set(date);
104    }
105    
106    public void verifyResults() {
107      Date result = resultRef.get();
108      if (expectValid) {
109        assertNotNull(result);
110      } else {
111        assertNull(result);
112      }
113    }
114    
115  }
116}