diff --git a/.gitignore b/.gitignore index 32d204506..36da55578 100644 --- a/.gitignore +++ b/.gitignore @@ -47,8 +47,8 @@ local.properties ############################################################################# # Built application files -*.apk -*.ap_ +#*.apk +#*.ap_ # Files for the Dalvik VM *.dex diff --git a/README.md b/README.md index f991e94ef..de46cd17c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ POSA-14 ======= -This repository contains assignments and examples for the 2014 offering of the POSA MOOC (see www.coursera.org/course/posa for more information) +This repository contains assignments and examples for the 2014 +offering of the POSA MOOC (see www.coursera.org/course/posa for more +information). diff --git a/ex/PingPong/console/.classpath b/assignments/week-1-assignment-0-v2/.classpath similarity index 79% rename from ex/PingPong/console/.classpath rename to assignments/week-1-assignment-0-v2/.classpath index 18d70f02c..b10b8d9b2 100644 --- a/ex/PingPong/console/.classpath +++ b/assignments/week-1-assignment-0-v2/.classpath @@ -2,5 +2,6 @@ + diff --git a/assignments/week-1-assignment-0-v2/.project b/assignments/week-1-assignment-0-v2/.project new file mode 100644 index 000000000..f4aa3946a --- /dev/null +++ b/assignments/week-1-assignment-0-v2/.project @@ -0,0 +1,17 @@ + + + W1-A0-SynchronizedQueueTestv2 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/assignments/week-1-assignment-0-v2/Assignment-Description.txt b/assignments/week-1-assignment-0-v2/Assignment-Description.txt new file mode 100644 index 000000000..42d3583a3 --- /dev/null +++ b/assignments/week-1-assignment-0-v2/Assignment-Description.txt @@ -0,0 +1,98 @@ +Week 1: Programming Assignment 0 v2 + +[Note, this is a different skeleton for assignment 0, which fixes some +problems with the "official" version, provides better diagnostics, and +also has a cleaner design that uses the Template Method pattern to +decouple the test infrastructure from the student-supplied +code. However, it is NOT the offical version that will be peer graded +and is provided solely as a way for students to understand better how +concurrency and Java Threads work.] + +In this (intentionally simple) initial assignment, you will use Java +Threads to test several implementations of the Java BlockingQueue +interface. The goal is to learn how to (1) create, (2) start, (3) +interrupt, and (4) wait for the completion of multiple Java Threads. +The test program also illustrates some problems that can occur if Java +Threads concurrently access an object that isn't synchronized +properly. + +In the "src/edu/vuum/mocca" folder in this directory you'll find the +SynchronizedQueueImpl.java class, which contains the skeleton Java +code that you'll implement by completing the "TODO - You fill in here" +comments to provide a working solution. DO NOT CHANGE THE OVERALL +STRUCTURE OF THE SKELETON - just fill in the "TODO - You fill in here" +portions!!! + +In particular, you'll need to do the following: + +. Implement the "TODO" portions of the createThreads() and + startThreads() methods so that the two Java mProducer and mConsumer + Threads are created and started to run the mProducerRunnable and + mConsumerRunnable objects. Please keep the "TODO" comments in the + code so that peer reviewers can find them quickly during the + assessment phase. + +. Implement the "TODO" portion of the interruptThreads() method, which + interrupts both the mProducer and mConsumer Threads so they will + shutdown. + +. Implement the "TODO" portion of the "joinThreads()" method, which + waits for both Threads to exit before continuing with the test + (which is done for you by the testQueue() template method in the + SynchronizedQueue.java file). + +. If you'd like to enable verbose debugging output please set the + diagnosticsEnabled flag in SynchronizedQueueImpl.java to true (it + defaults to false). + +All the information needed to write this code is described in these +videos: + + Section 1: Module 2: Part 1: Overview of Java Threads (Part 1) + Section 1: Module 2: Part 2: Overview of Java Threads (Part 2) + Section 1: Module 2: Part 3: Motivating Java Synchronization and Scheduling Mechanisms + +Make sure to watch these videos and read all the supplied Java code +carefully prior to starting the assignment. These videos are +available at + +https://class.coursera.org/posa-002/lecture + +We'll also discuss this assignment specification (and later its +solution) in the POSA MOOC "Virtual Office Hours", which are described +in item #38 at the POSA MOOC FAQ available from + +http://www.coursera.org/course/posa + +The SynchronizerQueueTest.java file uses JUnit to run the tests. We +do this to automate the testing process and leverage the integration +of JUnit with Eclipse, as described here: + +http://www.vogella.com/tutorials/JUnit/article.html#eclipse + +When you first open the project in Eclipse, you might see compile +errors if JUnit is not included in your build path. To fix these +errors, open SynchronizedQueueTest.java, hover over "org.junit," and +click "Fix project setup." Make sure "Add JUnit 4 library to the +build path" is selected and then click "OK." At this point, the +compile errors should disappear! + +To run the JUnit tests in Eclipse, right-click on +SynchronizedQueueTest.java and go to "Run As > JUnit Test". The JUnit +view will pop up in Eclipse and run the tests contained therein. All +tests should pass. The ArrayBlockingQueue will pass because your +testQueue method runs successfully. The tests for BuggyBlockingQueue +(which is an intentionally flawed class), should "pass" if some error +occurs while running testQueue (these errors are expected). If a test +passes a green-check mark will appear next to the test in the JUnit +view. As long as this JUnit test "passes" successfully your program +will be be consider "correct" for the purposes of assessing this +assignment. + +If the tests run and "pass," you should see check-marks next to each +of the tests in the JUnit view. If you set the diagnosticsEnabled +flag to true then debugging output will be printed to the console as +the tests run. This debugging output is for informational purposes +only and have no bearing on whether your program is "correct." If the +tests run and "pass," you should see a green check-mark next to the +test in the JUnit view, which is all that matters. diff --git a/assignments/week-1-assignment-0/src/edu/vuum/mooca/BuggyBlockingQueue.java b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/BuggyBlockingQueue.java similarity index 96% rename from assignments/week-1-assignment-0/src/edu/vuum/mooca/BuggyBlockingQueue.java rename to assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/BuggyBlockingQueue.java index be2ea68f2..2cfebfcc6 100644 --- a/assignments/week-1-assignment-0/src/edu/vuum/mooca/BuggyBlockingQueue.java +++ b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/BuggyBlockingQueue.java @@ -27,6 +27,13 @@ public BuggyBlockingQueue(int initialSize) { mList = new ArrayList(initialSize); } + /** + * Returns the number of elements in this queue. + */ + public int size() { + return mList.size(); + } + /** * Insert msg at the tail of the queue, but doesn't block if the * queue is full. @@ -119,7 +126,4 @@ public Iterator iterator() { public boolean isEmpty() { return false; } - public int size() { - return 0; - } } diff --git a/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueue.java b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueue.java new file mode 100644 index 000000000..bd6a10c9f --- /dev/null +++ b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueue.java @@ -0,0 +1,348 @@ +package edu.vuum.mooca; +import java.util.concurrent.*; + +/** + * @class SynchronizedQueue + * + * @brief This class tests the use of Java Threads and several + * implementations of the Java BlockingQueue interface. It + * plays the role of the Abstract Class in the Template Method + * pattern. + */ +public abstract class SynchronizedQueue { + /** + * Keep track of the number of times the producer test iterates. + */ + static volatile int mProducerCounter = 0; + + /** + * Keep track of the number of times the consumer test iterates. + */ + static volatile int mConsumerCounter = 0; + + /** + * Maximum timeout. + */ + static final int TIMEOUT_SECONDS = 5; + + /** + * Error value for a timeout. + */ + static final int TIMEOUT_OCCURRED = -1; + + /** + * Error value for a failure. + */ + static final int FAILURE_OCCURRED = -2; + + /** + * @class SynchronizedQueueResult + * + * @brief Enumerated type for return values of testing logic, has + * String for easy output. + */ + public enum SynchronizedQueueResult { + RAN_PROPERLY("Threads Ran Properly."), + JOIN_NEVER_CALLED("Join() never called."), + THREADS_NEVER_RAN("Threads never ran."), + THREADS_NEVER_INTERUPTED("Threads never interrupted."), + THREADS_THREW_EXCEPTION("Thread threw an exception."), + THREADS_NEVER_CREATED("Threads never created."), + TESTING_LOGIC_THREW_EXCEPTION("Testing Logic threw Exception."), + THREADS_TIMEDOUT("Threads Timed-out, Interupt likely not called."), + INCORRECT_COUNT("The size of mQueue is not consistent with the number of puts() and takes() performed."); + + /** + * String value for the enumerated type. + */ + private String mValue = null; + + /** + * Initialize the mValue string. + */ + private SynchronizedQueueResult(String value) { + mValue = value; + } + + /** + * Return the mValue string. + */ + public String getString() { + return mValue; + } + } + + /** + * @class QueueAdapter + * + * @brief Applies a variant of the GoF Adapter pattern that + * enables us to test several implementations of the + * BlockingQueue interface. + */ + public static class QueueAdapter { + /** + * Stores the queue that we're adapting. + */ + private BlockingQueue mQueue; + + /** + * Store the queue that we're adapting. + */ + public QueueAdapter(BlockingQueue queue) { + mQueue = queue; + } + + /** + * Returns the number of elements in this queue. + */ + int size() { + return mQueue.size(); + } + + /** + * Insert msg at the tail of the queue. + * + * @throws TimeoutException and InterruptedException + */ + public void put(E msg) throws InterruptedException, TimeoutException { + // Keep track of how many times we're called. + boolean timeoutValue = mQueue.offer(msg, + TIMEOUT_SECONDS, + TimeUnit.SECONDS); + if (timeoutValue == false) + throw new TimeoutException(); + + mProducerCounter++; + } + + /** + * Remove msg from the head of the queue. + * + * @throws TimeoutException + * , InterruptedException + */ + public E take() throws InterruptedException, TimeoutException { + // Keep track of how many times we're called. + E rValue = mQueue.poll(TIMEOUT_SECONDS, + TimeUnit.SECONDS); + + if (rValue == null) + throw new TimeoutException(); + + mConsumerCounter++; + + return rValue; + } + } + + /** + * Adapter object used to test different BlockingQueue + * implementations. + */ + private static QueueAdapter mQueue = null; + + /** + * This runnable loops for mMaxIterations and calls put() on + * mQueue to insert the iteration number into the queue. + */ + protected static Runnable mProducerRunnable = new Runnable() { + public void run() { + for (int i = 0; i < mMaxIterations; i++) + try { + mQueue.put(i); + if (Thread.interrupted()) + throw new InterruptedException(); + } catch (InterruptedException e) { + if (diagnosticsEnabled) + System.out.println("Thread " + + Thread.currentThread().getId() + + " in test " + + mTestName + + " properly interrupted by " + + e.toString() + " in producerRunnable"); + // This isn't an error - it just means that + // we've been interrupted by the main Thread. + return; + } catch (TimeoutException e) { + if (diagnosticsEnabled) + System.out.println("Thread " + + Thread.currentThread().getId() + + " in test " + + mTestName + + " Exception " + + e.toString() + + " occurred in producerRunnable"); + // Indicate a timeout. + mProducerCounter = TIMEOUT_OCCURRED; + return; + } catch (Exception e) { + if (diagnosticsEnabled) + System.out.println("Thread " + + Thread.currentThread().getId() + + " in test " + + mTestName + + " Exception " + + e.toString() + + " occurred in producerRunnable"); + // Indicate a failure. + mProducerCounter = FAILURE_OCCURRED; + return; + } + } + }; + + /** + * This runnable loops for mMaxIterations and calls take() on mQueue to + * remove the iteration from the queue. + */ + protected static Runnable mConsumerRunnable = new Runnable() { + public void run() { + for (int i = 0; i < mMaxIterations; i++) + try { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + Integer result = (Integer) mQueue.take(); + + if (diagnosticsEnabled) + System.out.println("iteration = " + result); + } catch (InterruptedException e) { + if (diagnosticsEnabled) + System.out.println("Thread " + + Thread.currentThread().getId() + + " in test " + + mTestName + + " properly interrupted by " + + e.toString() + " in consumerRunnable"); + // This isn't an error - it just means that + // we've been interrupted by the main Thread. + return; + } catch (TimeoutException e) { + if (diagnosticsEnabled) + System.out.println("Thread " + + Thread.currentThread().getId() + + " in test " + + mTestName + + " Exception " + + e.toString() + + " occurred in consumerRunnable"); + // Indicate a timeout. + mConsumerCounter = TIMEOUT_OCCURRED; + return; + } catch (Exception e) { + if (diagnosticsEnabled) + System.out.println("Thread " + + Thread.currentThread().getId() + + " in test " + + mTestName + + " Exception " + + e.toString() + + " occurred in consumerRunnable"); + // Indicate a failure. + mConsumerCounter = FAILURE_OCCURRED; + return; + } + } + }; + + protected SynchronizedQueueResult checkResults() { + int numberOfRemainingItemsInQueue = + mProducerCounter - mConsumerCounter; + + // Do some sanity checking to see if the Threads work as + // expected. + if (mConsumer == null + || mProducer == null) + return SynchronizedQueueResult.THREADS_NEVER_CREATED; + else if (mConsumer.isAlive() + || mProducer.isAlive()) + return SynchronizedQueueResult.JOIN_NEVER_CALLED; + else if (mConsumerCounter == 0 + || mProducerCounter == 0) + return SynchronizedQueueResult.THREADS_NEVER_RAN; + else if (mConsumerCounter == mMaxIterations + || mProducerCounter == mMaxIterations) + return SynchronizedQueueResult.THREADS_NEVER_INTERUPTED; + else if (mConsumerCounter == FAILURE_OCCURRED + || mProducerCounter == FAILURE_OCCURRED) + return SynchronizedQueueResult.THREADS_THREW_EXCEPTION; + else if (mConsumerCounter == TIMEOUT_OCCURRED + || mProducerCounter == TIMEOUT_OCCURRED) + return SynchronizedQueueResult.THREADS_TIMEDOUT; + else if (mQueue.size() != numberOfRemainingItemsInQueue) + return SynchronizedQueueResult.INCORRECT_COUNT; + else + return SynchronizedQueueResult.RAN_PROPERLY; + } + + /** + * Number of iterations to test (the actual test shouldn't run + * this many iterations since the Threads ought to be interrupted + * long before it gets this far). + */ + public static int mMaxIterations = 1000000; + + /** + * The Java Threads that are used to produce and consume messages + * on the queue. + */ + protected Thread mConsumer = null; + protected Thread mProducer = null; + + /** + * The name of the test that's being run, e.g., + */ + protected static String mTestName = null; + + /** + * If this is set to true in SynchronizedQueueImpl.java then lots + * of debugging output will be generated. + */ + public static boolean diagnosticsEnabled; + + /** + * These are hook methods that play the role of "primitive + * operations" in the Template Method pattern. They must be + * defined in SynchronizedQueueImpl.java by adding code after the + * "TODO" comments. + */ + protected abstract void createThreads(); + protected abstract void startThreads(); + protected abstract void interruptThreads(); + protected abstract void joinThreads() throws InterruptedException; + + /** + * This template method runs the test on the queue parameter. It + * decouples the test code from the user-defined code using the + * Template Method pattern. + */ + public SynchronizedQueueResult testQueue(QueueAdapter queue, + String testName) { + try { + mQueue = queue; + mTestName = testName; + mProducerCounter = 0; + mConsumerCounter = 0; + + // Invoke the various hook methods, which are "primitive + // operations" in the Template Method pattern. + createThreads(); + startThreads(); + + // Give the Threads a chance to run before interrupting + // them. (disabling console output makes them run really + // fast). + if (diagnosticsEnabled) + Thread.sleep(1000); + else + Thread.sleep(100); + + interruptThreads(); + joinThreads(); + + return checkResults(); + } catch (Exception e) { + return SynchronizedQueueResult.TESTING_LOGIC_THREW_EXCEPTION; + } + } +} diff --git a/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueImpl.java b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueImpl.java new file mode 100644 index 000000000..c34c74bb7 --- /dev/null +++ b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueImpl.java @@ -0,0 +1,45 @@ +package edu.vuum.mooca; + +/** + * @class SynchronizedQueueImpl + * + * @brief This is where you put your implementation code to to (1) + * create, (2) start, (3) interrupt, and (4) wait for the + * completion of multiple Java Threads. This class plays the + * role of the "Concrete Class" in the Template Method pattern + * and isolates the code written by students from the + * underlying SynchronizedQueue test infrastructure. + * + * Make sure to keep all the "TODO" comments in the code below + * to make it easy for peer reviewers to find them. + */ +public class SynchronizedQueueImpl extends SynchronizedQueue { + // TODO - change this to true if you want to see diagnostic + // output on the console as the test runs. + static { + diagnosticsEnabled = false; + } + + protected void createThreads() { + // TODO - replace the "null" assignments below to create two + // Java Threads, one that's passed the mProducerRunnable and + // the other that's passed the mConsumerRunnable. + mConsumer = null; + mProducer = null; + } + + protected void startThreads() { + // TODO - you fill in here to start the threads. More + // interesting results will occur if you start the + // consumer first. + } + + protected void interruptThreads() { + // TODO - you fill in here to interrupt the threads. + } + + protected void joinThreads() throws InterruptedException { + // TODO - you fill in here to wait for the threads to + // exit. + } +} diff --git a/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueTest.java b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueTest.java new file mode 100644 index 000000000..aa67caa4d --- /dev/null +++ b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueTest.java @@ -0,0 +1,121 @@ +package edu.vuum.mooca; +import static org.junit.Assert.*; + +import java.util.concurrent.ArrayBlockingQueue; + +import org.junit.Before; +import org.junit.Test; + +import edu.vuum.mooca.SynchronizedQueue.*; + +/** + * @class SynchronizedQueueTest + * + * @brief This class tests queues for proper functionality by + * using the testQueue method defined in SynchronizedQueue.java + */ +public class SynchronizedQueueTest { + /** + * Indicates how big the queue should be. + */ + int mQueueSize; + + /** + * Run the test for the queue parameter. + * + * @return result. If SynchronizedQueue test ran properly, returns + * null. If not, returns error message. + */ + static SynchronizedQueueResult runQueueTest(String qName, + QueueAdapter queue) { + if (SynchronizedQueue.diagnosticsEnabled) { + System.out.println("Starting " + + qName + + " test..."); + if (qName == "BuggyBlockingQueue") + System.out.println("An exception may be thrown since " + + qName + + " is intentially BUGGY."); + } + + /** + * We have to instantiate this object because Java doesn't + * like things being abstract AND static, which makes + * implementing the Template Pattern statically more painful + * than it should be. + */ + SynchronizedQueueResult result = + new SynchronizedQueueImpl().testQueue(queue, qName); + + if (SynchronizedQueue.diagnosticsEnabled) { + System.out.println("End " + qName + " test.\n"); + System.out.println("See JUnit view for results -- \n" + + "Green check-marks denote program correctness. \n" + + "Blue x-marks indicate a problem with your implementation. \n"); + } + + if (result != SynchronizedQueueResult.RAN_PROPERLY) + return result; + else + return null; + } + + /** + * Runs before each test. Sets mQueueSize. + * @throws Exception + */ + @Before + public void setUp() throws Exception { + // Indicate how big the queue should be, which should be + // smaller than the number of iterations to induce blocking + // behavior. + mQueueSize = SynchronizedQueue.mMaxIterations / 10; + } + + /** + * Tests the ArrayBlockingQueue, which should pass without error. + */ + @Test + public void arrayBlockingQueueTest() { + // Make the appropriate QueueAdapter for the + // ArrayBlockingQueue. + QueueAdapter queueAdapter = + new QueueAdapter(new ArrayBlockingQueue(mQueueSize)); + + // Run a test on the ArrayBlockingQueue. + SynchronizedQueueResult errors = + runQueueTest("ArrayBlockingQueue", queueAdapter); + + String errorMessage = ""; + + if (errors != null) + errorMessage = errors.getString(); + + assertNull("Error occurred: " + + errorMessage, + errors); + } + + /** + * Tests the BuggyBlockingQueue, an intentionally flawed class. + * The buggyBlockingQueueTest() will succeed if the testQueue + * method fails, i.e., this test succeeds if our queue causes + * errors (which is what we expect)! + */ + @Test + public void buggyBlockingQueueTest() { + // Make the appropriate QueueAdapter for the + // BuggyBlockingQueue. + + QueueAdapter queueAdapter = + new QueueAdapter(new BuggyBlockingQueue(mQueueSize)); + + // Run a test on the BuggyBlockingQueue. + SynchronizedQueueResult errors = + runQueueTest("BuggyBlockingQueue", queueAdapter); + + assertNotNull("Test should not complete without errors. " + + "BuggyBlockingQueue is intended to function incorrectly.", + errors); + } +} diff --git a/assignments/week-1-assignment-0/.project b/assignments/week-1-assignment-0/.project index 1108d1f6e..5b2ee43b7 100644 --- a/assignments/week-1-assignment-0/.project +++ b/assignments/week-1-assignment-0/.project @@ -1,6 +1,6 @@ - SynchronizedQueueTest + W1-A0-SynchronizedQueueTest diff --git a/assignments/week-1-assignment-0/Assignment-Description.txt b/assignments/week-1-assignment-0/Assignment-Description.txt index e485c2765..76af1a687 100644 --- a/assignments/week-1-assignment-0/Assignment-Description.txt +++ b/assignments/week-1-assignment-0/Assignment-Description.txt @@ -1,4 +1,4 @@ -Week 1: Programming Assignment 0 +Week 1: Programming Assignment 0 Description Released Monday, May 12, 2014 Due Monday, May 26th, 2014 @@ -50,7 +50,7 @@ We'll also discuss this assignment specification (and later its solution) in the POSA MOOC "Virtual Office Hours", which are described in item #38 at the POSA MOOC FAQ available from -http://www.courera.org/course/posa +http://www.coursera.org/course/posa The SynchronizerQueueTest.java file uses JUnit to run the tests. We do this to automate the testing process and leverage the integration @@ -67,16 +67,15 @@ compile errors should disappear! To run the JUnit tests in Eclipse, right-click on SynchronizedQueueTest.java and go to "Run As > JUnit Test". The JUnit -view will pop up in Eclipse and run the tests contained therein. All -tests should pass. The ArrayBlockingQueue will pass because your -testQueue method runs successfully. The tests for BuggyBlockingQueue -(which is an intentionally flawed class), should "pass" if some error -occurs while running testQueue (these errors are expected). As long -as these JUnit tests both "pass" successfully your program will be be -consider "correct" for the purposes of assessing this assignment. - -If the tests run and "pass," you should see check-marks next to each -of the tests in the JUnit view. As the tests run, you will also find -output being printed to the console. This text is for informational -purposes only and have no bearing on whether your program is -"correct." +view will pop up in Eclipse and run the test contained therein. If +your testQueue method runs successfully the ArrayBlockingQueue should +pass, i.e., a green-check mark will appear next to the test in the +JUnit view. As long as this JUnit test "passes" successfully your +program will be be consider "correct" for the purposes of assessing +this assignment. + +As the tests run, you will also find debugging output being printed to +the console. This debugging output is for informational purposes only +and have no bearing on whether your program is "correct." If the +tests run and "pass," you should see a green check-mark next to the +test in the JUnit view, which is all that matters. diff --git a/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java b/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java index c8a8c5edc..c4ffb3c18 100644 --- a/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java +++ b/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java @@ -186,7 +186,7 @@ public void run() { System.out.println("Exception " + e.toString() + " occurred in consumerRunnable"); // Indicate a timeout. - mProducerCounter = TIMEOUT_OCCURRED; + mConsumerCounter = TIMEOUT_OCCURRED; return; } catch (Exception e) { System.out.println("Exception " + e.toString() diff --git a/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java b/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java index 2a459546b..427448946 100644 --- a/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java +++ b/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java @@ -68,21 +68,4 @@ public void arrayBlockingQueueTest() { errors, errors); } - - /** - * Tests the BuggyBlockingQueue, an intentionally flawed class. - * The buggyBlockingQueueTest() will succeed if the testQueue - * method fails, i.e., this test succeeds if our queue causes - * errors (which is what we expect)! - */ - @Test - public void buggyBlockingQueueTest() { - QueueAdapter queueAdapter = - new QueueAdapter(new BuggyBlockingQueue(queueSize)); - String errors = runQueueTest("BuggyBlockingQueue", queueAdapter); - assertNotNull("Test should not complete without errors. " + - "BuggyBlockingQueue is intended to function incorrectly.", - errors); - } - } diff --git a/assignments/week-2-assignment-1/.classpath b/assignments/week-2-assignment-1/.classpath index 18d70f02c..3e0fb272a 100644 --- a/assignments/week-2-assignment-1/.classpath +++ b/assignments/week-2-assignment-1/.classpath @@ -1,6 +1,7 @@ - + + diff --git a/assignments/week-2-assignment-1/Assignment-Description.txt b/assignments/week-2-assignment-1/Assignment-Description.txt index 5ce1096ec..cd35d72e6 100644 --- a/assignments/week-2-assignment-1/Assignment-Description.txt +++ b/assignments/week-2-assignment-1/Assignment-Description.txt @@ -1,27 +1,27 @@ -Programming Assignment 0 +Week 2: Programming Assignment 1 -In this first (intentionally simple) assignment, you will use a Java -ReentrantReadWriteLock to implement a subset of the -java.util.concurrent.atomic.AtomicLong class, which we call -SimpleAtomicLock. The goal is to understand how to use +Released Monday, May 19th, 2014 +Due Monday, June 2nd, 2014 + +In this second assignment, you'll implement a subset of the +java.util.concurrent.atomic.SimpleAtomicLong class using a Java +ReentrantReadWriteLock. The goal is to understand how to use ReentrantReadWriteLock to serialize access to a variable that's shared -by multiple threads. The SimpleAtomicLongTest.java program creates -two Threads that increment and decrement the AtomicLong 10,000,000 -times each. If the SimpleAtomicLong implementation is properly -serialized it's final value should be 0. +by multiple threads. The BuggyLongTest.java program shows what happens if concurrent access to a long isn't properly serialized. This test program works best on -a multi-core/multi-processor machine, which should actually induce -failure due to race conditions stemming from parallel execution. If -this test "succeeds" then your target platform is not sufficiently -parallel to demonstrate the bug. - -In this directory you'll find the SimpleAtomicLong.java class, which -contains the skeleton Java code that you'll implement by completing -the "TODO - You fill in here" comments to provide a working solution. -DO NOT CHANGE THE OVERALL STRUCTURE OF THE SKELETON - just fill in the -"TODO - You fill in here" portions!!! +a multi-core/multi-processor machine, which should induce a failure +due to race conditions stemming from parallel execution. If this test +"succeeds" then your target platform may not be sufficiently parallel +to demonstrate the race conditions. + +In the src/edu/vuum/mocca directory you'll find the +SimpleAtomicLong.java class, which contains the skeleton Java code +that you'll implement by completing the "TODO - You fill in here" +comments to provide a working solution. DO NOT CHANGE THE OVERALL +STRUCTURE OF THE SKELETON - just fill in the "TODO - You fill in here" +portions!!! In particular, you'll need to do the following: @@ -34,47 +34,45 @@ In particular, you'll need to do the following: http://tutorials.jenkov.com/java-util-concurrent/readwritelock.html -. Make sure to use a readLock() for AtomicLong.get() and writeLock() - for all the other SimpleAtomicLong methods. +. Make sure to use a readLock() for SimpleAtomicLong.get() and + writeLock() for all the other SimpleAtomicLong methods. -. The SimpleAtomicLongTest.java program uses various features of Java - Threads and Runnables, which are described in these videos: +Note that the SimpleAtomicLongTest.java program uses Java Threads and +Runnables, which are described in these videos: Section 1: Module 2: Part 1: Overview of Java Threads (Part 1) Section 1: Module 2: Part 2: Overview of Java Threads (Part 2) - Likewise, it also uses the Java CountDownLatch and CyclicBarrier - classes, which are described in this video: +Likewise, it also uses the Java CountDownLatch and CyclicBarrier +classes, which are described in this video: Section 1: Module 2: Part 9: Java CountDownLatch -At the moment, all these videos are available in the YouTube playlist -at - -https://www.youtube.com/playlist?list=PLZ9NgFYEMxp4tbiFYip6tDNIEBRUDyPQK - -When the 2014 POSA MOOC officially starts these videos will be -available at +These videos are available at https://class.coursera.org/posa-002/lecture -We'll also discuss this assignment specification (and later its -solution) in the POSA MOOC "Virtual Office Hours", which are described -in item #38 at the POSA MOOC FAQ available from +When you first open the project in Eclipse, you might see compile +errors if JUnit is not included in your build path. To fix these +errors, open SynchronizedQueueTest.java, hover over "org.junit," and +click "Fix project setup." Make sure "Add JUnit 4 library to the +build path" is selected and then click "OK." At this point, the +compile errors should disappear! + +To run the JUnit tests in Eclipse, right-click on +SimpleAtomicLongTests.java and Run As > JUnit Test. The JUnit view +will pop up in Eclipse and run the the tests, which are divided into +two subsets: multi-threaded and single-threaded tests. The +single-threaded tests verify functionality of all methods within a +single thread, while the multi-threaded tests do the same in a +multi-threaded environment. All tests should run successfully, i.e., +a green-check mark will appear next to the test in the JUnit view. As +long as this JUnit test "passes" successfully your program will be be +consider "correct" for the purposes of assessing this assignment. -http://www.courera.org/course/posa -To compile this code you can either use the provided Eclipse project -or simply type -% javac SimpleAtomicLockTest.java SimpleAtomicLock.java -on the command-line and then run the resulting class file by typing -% java SimpleAtomicLockTest -The output for a correct solution should look exactly like this: -Starting SimpleAtomicLongTest -test worked -Finishing SimpleAtomicLongTest diff --git a/assignments/week-2-assignment-1/src/SimpleAtomicLongTest.java b/assignments/week-2-assignment-1/src/SimpleAtomicLongTest.java deleted file mode 100644 index a86ed7d4d..000000000 --- a/assignments/week-2-assignment-1/src/SimpleAtomicLongTest.java +++ /dev/null @@ -1,132 +0,0 @@ -// Import the necessary Java synchronization and scheduling classes. - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.CyclicBarrier; - -/** - * @class SimpleAtomicLongTest - * - * @brief This class tests our implementation of SimpleAtomicLong to ensure - * it works properly. - */ -class SimpleAtomicLongTest -{ - /** - * Number of iterations to run the test. - */ - final static long mMaxIterations = 10000000; - - /** - * Barrier synchronizer that controls when the two threads start - * running the test. - */ - final static CyclicBarrier mStartBarrier = new CyclicBarrier(2); - - /** - * Barrier synchronizer that controls when the main thread can - * return. - */ - final static CountDownLatch mStopLatch = new CountDownLatch(2); - - /** - * An instance of our implementation of SimpleAtomicLong. - */ - final static SimpleAtomicLong mCounter = new SimpleAtomicLong(0); - - /** - * @brief This class runs the test by invoking a command each time - * through the loop. - */ - static class RunTest implements Runnable - { - /** - * A Command that determines what operation is done within the - * loop. - */ - private Runnable mCommand; - - /** - * Store the command in a data member field. - */ - RunTest(Runnable command) { - mCommand = command; - } - - /** - * Run the command within a loop. - */ - public void run() { - try - { - /** - * Wait for both Threads to start running before - * beginning the loop. - */ - mStartBarrier.await(); - - for (int i = 0; i < mMaxIterations; ++i) - mCommand.run(); - - /** - * Inform the main thread that we're done. - */ - mStopLatch.countDown(); - } - catch (Exception e) { - System.out.println("problem here"); - } - } - } - - /** - * Main entry point method that runs the test. - */ - public static void main(String[] args) { - try { - System.out.println("Starting SimpleAtomicLongTest"); - - /** - * A Runnable command that decrements the mCounter. - */ - final Runnable decrementCommand = - new Runnable() { public void run() { mCounter.getAndDecrement(); } }; - - /** - * A Runnable command that decrements the mCounter. - */ - final Runnable incrementCommand = - new Runnable() { public void run() { mCounter.getAndIncrement(); } }; - - /** - * Start a Thread whose Runnable command decrements the - * SimpleAtomicLong mMaxIterations number of times. - */ - new Thread(new RunTest(decrementCommand)).start(); - - /** - * Start a Thread whose Runnable command increments the - * SimpleAtomicLong mMaxIterations number of times. - */ - new Thread(new RunTest(incrementCommand)).start(); - - /** - * Barrier synchronizer that waits for both worker threads - * to exit before continuing. - */ - mStopLatch.await(); - - long result = mCounter.get(); - /** - * Check to ensure the test worked, i.e., mCounter's value - * should be 0. - */ - if (result == 0) - System.out.println("test worked"); - else - System.out.println("test failed: mCounter = " + result); - - System.out.println("Finishing SimpleAtomicLongTest"); - } - catch (Exception e) { } - } -} diff --git a/assignments/week-2-assignment-1/src/BuggyLongTest.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java similarity index 92% rename from assignments/week-2-assignment-1/src/BuggyLongTest.java rename to assignments/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java index 74c467b24..fc854689b 100644 --- a/assignments/week-2-assignment-1/src/BuggyLongTest.java +++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java @@ -1,4 +1,4 @@ -// Import the necessary Java synchronization and scheduling classes. +package edu.vuum.mocca; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; @@ -14,7 +14,7 @@ class BuggyLongTest /** * Number of iterations to run the test. */ - static final long mMaxIterations = 10000000; + static final long mMaxIterations = 100000000; /** * Barrier synchronizer that controls when the two threads start @@ -41,7 +41,7 @@ public static void main(String[] args) { System.out.println("Starting BuggyLongTest"); /** - * Start a Thread whose Runnable decrements the AtomicLong + * Start a Thread whose Runnable decrements the SimpleAtomicLong * mMaxIterations number of times. */ new Thread(new Runnable() @@ -70,7 +70,7 @@ public static void main(String[] args) { }).start(); /** - * Start a Thread whose Runnable increments the AtomicLong + * Start a Thread whose Runnable increments the SimpleAtomicLong * mMaxIterations number of times. */ new Thread(new Runnable() diff --git a/assignments/week-2-assignment-1/src/SimpleAtomicLong.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java similarity index 59% rename from assignments/week-2-assignment-1/src/SimpleAtomicLong.java rename to assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java index fc781e4ee..f57064f95 100644 --- a/assignments/week-2-assignment-1/src/SimpleAtomicLong.java +++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java @@ -1,5 +1,7 @@ // Import the necessary Java synchronization and scheduling classes. +package edu.vuum.mocca; + import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.Lock; @@ -16,31 +18,27 @@ class SimpleAtomicLong * The value that's manipulated atomically via the methods. */ private long mValue; - + + /** * The ReentrantReadWriteLock used to serialize access to mValue. */ - // TODO - replace the null with the appropriate initialization: - private ReentrantReadWriteLock mRWLock = null; + // TODO - add the implementation /** * Creates a new SimpleAtomicLong with the given initial value. */ - public SimpleAtomicLong(long initialValue) - { + public SimpleAtomicLong(long initialValue) { // TODO - you fill in here } /** - * @brief Gets the current value. + * @brief Gets the current value * * @returns The current value */ - public long get() - { - long value; - // TODO - you fill in here, using a readLock() - return value; + public long get() { + // TODO - you fill in here } /** @@ -48,11 +46,8 @@ public long get() * * @returns the updated value */ - public long decrementAndGet() - { - long value; - // TODO - you fill in here, using a writeLock() - return value; + public long decrementAndGet() { + // TODO - you fill in here } /** @@ -60,11 +55,8 @@ public long decrementAndGet() * * @returns the previous value */ - public long getAndIncrement() - { - long value; - // TODO - you fill in here, using a writeLock() - return value; + public long getAndIncrement() { + // TODO - you fill in here } /** @@ -72,11 +64,8 @@ public long getAndIncrement() * * @returns the previous value */ - public long getAndDecrement() - { - long value; - // TODO - you fill in here, using a writeLock() - return value; + public long getAndDecrement() { + // TODO - you fill in here } /** @@ -84,11 +73,8 @@ public long getAndDecrement() * * @returns the updated value */ - public long incrementAndGet() - { - long value; - // TODO - you fill in here, using a writeLock() - return value; + public long incrementAndGet() { + // TODO - you fill in here } } diff --git a/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java new file mode 100644 index 000000000..afde62c6f --- /dev/null +++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java @@ -0,0 +1,322 @@ +package edu.vuum.mocca; + +import static org.junit.Assert.*; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * @class SimpleAtomicLongMultithreadedTest + * + * @brief Test the logic and multithreaded implementation of the + * SimpleAtomicLong class by having concurrent threads call the + * SimpleAtomicLong instance for various methods. + */ +public class SimpleAtomicLongMultithreadedTest { + + /** + * Our start points. + */ + final static long INITIAL_VALUE = 0; + + /** + * Number of iterations to run the commands. + */ + final static long mMaxIterations = 1000000; + + /** + * Barrier synchronizer that controls when the threads start + * running the test. + */ + static CyclicBarrier mStartBarrier; + + /** + * Barrier synchronizer that controls when the main thread can + * return. + */ + static CountDownLatch mStopLatch; + + /** + * An instance of our implementation of SimpleAtomicLong, which is + * defined as "volatile" to ensure proper visibility of its fields + * after construction. + */ + static volatile SimpleAtomicLong mCounter; + + /** + * Runnable commands that use the mCounter methods + * get() + * incrementAndGet() + * getAndIncrement() + * decrementAndGet() + * getAndDecrement() + */ + static Runnable getCommand; + static Runnable incrementGetCommand; + static Runnable getIncrementCommand; + static Runnable decrementGetCommand; + static Runnable getDecrementCommand; + + /** + * The value of mCounter prior to any changes made by testing. + */ + long preTestValue; + + /** + * The number of duplicate threads to run when testing each + * individual command. + */ + final int numThreads = 5; + + /** + * @class RunTest + * + * @brief This class runs the test by invoking a command each time + * through the loop. + */ + static class RunTest implements Runnable + { + /** + * A Command that determines what operation is done within the + * loop. + */ + private Runnable mCommand; + + /** + * An integer which keeps track of the number of times the + * command has been called. Initially equal to zero. + */ + private long iterations = 0; + + /** + * Store the command in a data member field. + */ + RunTest(Runnable command) { + mCommand = command; + } + + /** + * Run the command within a loop. + */ + public void run() { + try + { + /** + * Wait for all Threads to start running before + * beginning the loop. + */ + mStartBarrier.await(); + + for (; iterations < mMaxIterations; ++iterations) { + mCommand.run(); + } + /** + * Inform the main thread that we're done. + */ + mStopLatch.countDown(); + } catch (Exception e) { + fail("Runnable failed."); + } + } + + /** + * Returns the number of times this command has been performed. + * @return iterations + */ + public long getIterations() { + return iterations; + } + } + + /** + * Runs prior to all tests. Creates a static instance of + * SimpleAtomicLong and all runnable commands. + */ + @BeforeClass + public static void setUpBeforeClass() throws Exception { + /** + * Instance of SimpleAtomicLong class + */ + mCounter = new SimpleAtomicLong(INITIAL_VALUE); + + /** + * Runnable commands that execute get(), incrementAndGet(), + * getAndIncrement(), decrementAndGet(), getAndDecrement(), + * respectively, on the SimpleAtomicLong instance + */ + getCommand = new Runnable() { public void run() { mCounter.get(); } }; + incrementGetCommand = new Runnable() { public void run() { mCounter.incrementAndGet(); } }; + getIncrementCommand = new Runnable() { public void run() { mCounter.getAndIncrement(); } }; + decrementGetCommand = new Runnable() { public void run() { mCounter.decrementAndGet(); } }; + getDecrementCommand = new Runnable() { public void run() { mCounter.getAndDecrement(); } }; + } + + /** + * Runs prior to each test. Stores the pre-test value of the mCounter. + */ + @Before + public void setUp() throws Exception { + preTestValue = mCounter.get(); + } + + /** + * Tests for proper concurrency and functionality of {@code get()}. + */ + @Test + public void multiGetTest() { + /** + * run multiple threads calling mCounter.get(). + */ + runThreads(getCommand); + /** + * The expected post-test value is no change in the pre-test + * value. + */ + assertEquals(preTestValue, + mCounter.get()); + } + + /** + * Tests for proper concurrency and functionality of {@code + * incrementAndGet()}. + */ + @Test + public void multiIncrementAndGetTest() { + runThreads(incrementGetCommand); + /** + * expected value after threads are run should be the number + * of maximum iterations times the number of threads plus the + * pre-test value + */ + assertEquals(preTestValue + + mMaxIterations*numThreads, + mCounter.get()); + } + + /** + * Tests for proper concurrency and functionality of {@code getAndIncrement()}. + */ + @Test + public void multiGetAndIncrementTest() { + runThreads(getIncrementCommand); + assertEquals(preTestValue + + mMaxIterations*numThreads, + mCounter.get()); + } + + /** + * Tests for proper concurrency and functionality of {@code + * decrementAndGet()}. + */ + @Test + public void multiDecrementAndGetTest() { + runThreads(decrementGetCommand); + /** + * Expected value of mCounter after threads have completed + * running is the pre-test value minus the maximum iterations + * times the number of threads that were run. + */ + assertEquals(preTestValue - + mMaxIterations*numThreads, + mCounter.get()); + } + + /** + * Tests for proper concurrency and functionality of {@code getAndIncrement()}. + */ + @Test + public void multiGetAndDecrementTest() { + runThreads(getDecrementCommand); + /** + * expected value of mCounter after threads have completed running + * is the pre-test value minus the maximum iterations times the number + * of threads that were run + */ + assertEquals(preTestValue - + mMaxIterations*numThreads, + mCounter.get()); + } + + /** + * Tests concurrent running of threads performing a variety of + * operations on mCounter (e.g. {@code get()}, {@code + * getAndIncrement()}, {@code getAndDecrement()},{@code + * incrementAndGet()}, and {@code decrementAndGet()}). + */ + @Test + public void multiThreadedTest() { + /** + * Run five threads concurrently, each performing a different + * method on the SimpleAtomicLong instance + */ + runThreads(null); + /** + * Check to ensure the pre-test and post-test values are + * equal. This indicates the test was successful. + */ + assertEquals(preTestValue, + mCounter.get()); + } + + /** + * Runs numThreads concurrent threads executing the same command. + * Has a CyclicBarrier and CountDownLatch to facilitate + * concurrency. + * @param command + */ + private void runThreads(Runnable command) { + mStartBarrier = new CyclicBarrier(numThreads); + mStopLatch = new CountDownLatch(numThreads); + try { + /** + * Create an array of RunTests whose Runnable commands + * execute on the SimpleAtomicLong mMaxIterations number + * of times. If given a command, each thread should run + * duplicates. If command is null, then run one of each + * type of command. + */ + RunTest[] runTests; + if(command == null) { + runTests = new RunTest[5]; + runTests[0] = new RunTest(getCommand); + runTests[1] = new RunTest(incrementGetCommand); + runTests[2] = new RunTest(decrementGetCommand); + runTests[3] = new RunTest(getDecrementCommand); + runTests[4] = new RunTest(getIncrementCommand); + } + else { + runTests = new RunTest[numThreads]; + for(int i = 0; i < runTests.length; i++) + runTests[i] = new RunTest(command); + } + + /** + * Start threads whose Runnable commands execute on the + * SimpleAtomicLong mMaxIterations number of times. + */ + for(int i = 0; i < runTests.length; i++) + new Thread(runTests[i]).start(); + + /** + * Barrier synchronizer that waits for all worker threads + * to exit before continuing. + */ + mStopLatch.await(); + + /** + * Check to ensure threads have run. + */ + for(int i = 0; i < runTests.length; i++) + assertEquals("Threads have not executed.", + mMaxIterations, + runTests[i].getIterations()); + } catch (Exception e) { + fail("Exception thrown."); + } + } + +} diff --git a/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java new file mode 100644 index 000000000..ef28e3e0a --- /dev/null +++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java @@ -0,0 +1,235 @@ +package edu.vuum.mocca; + +import static org.junit.Assert.assertEquals; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @brief SimpleAtomicLongSingleThreadedTest + * + * @class Evalutes the logic of the SimpleAtomicLong class by testing + * every method with each of the values in mTestValues. + */ +public class SimpleAtomicLongSingleThreadedTest { + /* + * Test possible edge cases at 0, and a large negative and + * positive. + */ + final static long[] mTestValues = { -100, -1, 0, 1, 100 }; + + /** + * Test Constructor. + */ + @Test + public void constructorTest() { + for (long testValue : mTestValues) { + SimpleAtomicLong counter = + new SimpleAtomicLong(testValue); + Assert.assertNotNull(counter); + assertEquals(testValue, counter.get()); + } + } + + /** + * Test get() + */ + @Test + public void getTest() { + for (long testValue : mTestValues) { + SimpleAtomicLong counter = new SimpleAtomicLong(testValue); + assertEquals(testValue, + counter.get()); + } + } + + /** + * test decrementAndGet() + */ + @Test + public void decrementAndGetTest() { + for (long testValue : mTestValues) { + final SimpleAtomicLong counter = + new SimpleAtomicLong(testValue); + decrementAndGetTestLogic(counter, + testValue, + testValue - 1, + testValue - 1); + } + } + + /** + * test getAndDecrement() + */ + @Test + public void getAndDecrementTest() { + for (long testValue : mTestValues) { + final SimpleAtomicLong counter = + new SimpleAtomicLong(testValue); + getAndDecrementTestLogic(counter, + testValue, + testValue, + testValue - 1); + } + } + + /** + * test incrementAndGet() + */ + @Test + public void incrementAndGetTestTest() { + for (long testValue : mTestValues) { + final SimpleAtomicLong counter = + new SimpleAtomicLong(testValue); + incrementAndGetTestLogic(counter, + testValue, + testValue + 1, + testValue + 1); + } + } + + /** + * test getAndIncrement() + */ + @Test + public void getAndIncrementTest() { + for (long testValue : mTestValues) { + final SimpleAtomicLong counter = + new SimpleAtomicLong(testValue); + getAndIncrementTestLogic(counter, + testValue, + testValue, + testValue + 1); + } + } + + /** + * Compares the values expected with the values produced by each test logic. + * + * @param pre + * The 'pre' number produced by the test + * @param preValue + * The 'pre' number expected + * @param result + * The 'result' number produced by the test + * @param resultValue + * The 'result' expected + * @param post + * The 'post' number produced by the test + * @param postValue + * The 'post' expected + */ + private void compareResults(long pre, long preValue, long result, + long resultValue, long post, long postValue) { + assertEquals(pre, preValue); + assertEquals(result, resultValue); + assertEquals(post, postValue); + } + + /** + * The Logic of testing decrementAndGet + * + * @param simpleAtomicLong + * The SimpleAtomicLong to be tested + * @param preValue + * The expected 'pre' value + * @param resultValue + * The expected 'result' value + * @param postValue + * The expected 'post' value + */ + public void decrementAndGetTestLogic(SimpleAtomicLong simpleAtomicLong, + long preValue, long resultValue, long postValue) { + long pre = simpleAtomicLong.get(); + long result = simpleAtomicLong.decrementAndGet(); + long post = simpleAtomicLong.get(); + assertEquals(pre - 1, result); + assertEquals(result, post); + compareResults(pre, + preValue, + result, + resultValue, + post, + postValue); + } + + /** + * The Logic of testing getAndDecrement + * + * @param simpleAtomicLong + * The SimpleAtomicLong to be tested + * @param preValue + * The expected 'pre' value + * @param resultValue + * The expected 'result' value + * @param postValue + * The expected 'post' value + */ + public void getAndDecrementTestLogic(SimpleAtomicLong simpleAtomicLong, + long preValue, long resultValue, long postValue) { + long pre = simpleAtomicLong.get(); + long result = simpleAtomicLong.getAndDecrement(); + long post = simpleAtomicLong.get(); + assertEquals(pre, result); + assertEquals(pre - 1, post); + compareResults(pre, + preValue, + result, + resultValue, + post, + postValue); + } + + /** + * The Logic of testing incrementAndGet + * + * @param simpleAtomicLong + * The SimpleAtomicLong to be tested + * @param preValue + * The expected 'pre' value + * @param resultValue + * The expected 'result' value + * @param postValue + * The expected 'post' value + */ + public void incrementAndGetTestLogic(SimpleAtomicLong simpleAtomicLong, + long preValue, long resultValue, long postValue) { + long pre = simpleAtomicLong.get(); + long result = simpleAtomicLong.incrementAndGet(); + long post = simpleAtomicLong.get(); + assertEquals(pre + 1, result); + assertEquals(result, post); + compareResults(pre, + preValue, + result, + resultValue, + post, + postValue); + } + + /** + * The Logic of testing getAndIncrement + * + * @param simpleAtomicLong + * The SimpleAtomicLong to be tested + * @param preValue + * The expected 'pre' value + * @param resultValue + * The expected 'result' value + * @param postValue + * The expected 'post' value + */ + public void getAndIncrementTestLogic(SimpleAtomicLong simpleAtomicLong, + long preValue, long resultValue, long postValue) { + long pre = simpleAtomicLong.get(); + long result = simpleAtomicLong.getAndIncrement(); + long post = simpleAtomicLong.get(); + assertEquals(pre, result); + assertEquals(pre + 1, post); + compareResults(pre, + preValue, + result, + resultValue, + post, postValue); + } +} diff --git a/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java new file mode 100644 index 000000000..990496762 --- /dev/null +++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java @@ -0,0 +1,18 @@ +package edu.vuum.mocca; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + + +@RunWith(Suite.class) +@SuiteClasses({ SimpleAtomicLongMultithreadedTest.class, + SimpleAtomicLongSingleThreadedTest.class }) +/** + * @class SimpleAtomicLongTest + * + * @brief Entry point for running all the regression tests for the + * SimpleAtomicLong implementation. + */ +public class SimpleAtomicLongTests { +} diff --git a/assignments/week-3-assignment-2/.classpath b/assignments/week-3-assignment-2/.classpath index 18d70f02c..b0e3db7ba 100644 --- a/assignments/week-3-assignment-2/.classpath +++ b/assignments/week-3-assignment-2/.classpath @@ -1,6 +1,8 @@ + + diff --git a/assignments/week-3-assignment-2/Assignment-Description.txt b/assignments/week-3-assignment-2/Assignment-Description.txt index 1a9d94c75..57c2ddf42 100644 --- a/assignments/week-3-assignment-2/Assignment-Description.txt +++ b/assignments/week-3-assignment-2/Assignment-Description.txt @@ -1,38 +1,49 @@ -Programming Assignment 0.5 +Week 3: Programming Assignment 2 + +Released Monday, May 26th, 2014 +Due Monday, June 9th, 2014 In this assignment, you will use a Java ReentrantLock and Java -ConditionObject to implement a subset of the Java +Condition to implement a subset of the Java java.util.concurrent.Semaphore class, which we call SimpleSemaphore. -This assignment also reuses the SimpleAtomicLock you implemented for -Assignment 0, so make sure it's working properly before attempting -this assignment! The goal of this assignment is to use the -SimpleSemaphore to correctly implement a resource manager that limits -the number of Beings from Middle-Earth who can concurrently gaze into -a Palantir (if you're not a Lord of the Ring's fan see +This assignment also reuses the SimpleAtomicLong you implemented for +week-2-assignment-1, so make sure it's compiling and running properly +before attempting this assignment! + +The goal of this assignment is to use the SimpleSemaphore to correctly +implement a resource manager that limits the number of Beings from +Middle-Earth who can concurrently gaze into a Palantir. Please see http://en.wikipedia.org/wiki/Palantir for more information on -Palantir). +Palantirs if you're not yet a fan of Tolkein's Lord of the Ring's. -The PalantirManagerTest.java program creates three Palantiri and five -Threads (one for each Palantir user) that concurrently attempt to +The PalantirManagerUnitTest.java program creates three Palantiri and +five Threads (one for each Palantir user) that concurrently attempt to acquire a Palantir and gaze into it for a certain amount of time. If -the SimpleSemaphore and SimpleAtomicLock are implemented properly the -test should succeed without throwing any exceptions. +the SimpleSemaphore and SimpleAtomicLong are implemented properly the +test should succeed without throwing any exceptions, as described +further below. In this directory you'll find a number of files, all of which you should read. However, the only two files you need to modify are -SimpleAtomicLong.java class and SimpleSemaphore, which contains the -skeleton Java code that you'll implement by completing the "TODO - You -fill in here" comments to provide a working solution. DO NOT CHANGE -THE OVERALL STRUCTURE OF THE SKELETON - just fill in the "TODO - You -fill in here" portions!!! +SimpleAtomicLong.java and SimpleSemaphore.java in the +src/edu/vuum/mocca directory, which contains the skeleton Java code +that you'll implement by completing the "TODO - You fill in here" +comments to provide a working solution. DO NOT CHANGE THE OVERALL +STRUCTURE OF THE SKELETON - just fill in the "TODO - You fill in here" +portions!!! In particular, you'll need to do the following: . Implement the SimpleAtomicLong class, which you should replace with - your working solution to assignment0. - -. Implement the SimpleSemaphore class using a Java ConditionObject and - Java ReentrantLock, which are covered in these videos: + your solution to week-2-assignment-1, after applying any fixes + motivated by watching the Virtual Office Hours video of the + instructor's solution(s). This class is only used by the + PalantirManagerUnitTest.java and should not be used in the + SimpleSemaphore implementation itself. + +. Implement the SimpleSemaphore class using a Java ConditionObject + (accessed via a Condition) and Java ReentrantLock, which are covered + in these videos: Section 1: Module 2: Part 5: Java ReentrantLock Section 1: Module 2: Part 8: Java ConditionObject @@ -58,13 +69,7 @@ In particular, you'll need to do the following: Section 1: Module 2: Part 1: Overview of Java Threads (Part 1) Section 1: Module 2: Part 2: Overview of Java Threads (Part 2) -At the moment, all these videos are available in the YouTube playlist -at - -https://www.youtube.com/playlist?list=PLZ9NgFYEMxp4tbiFYip6tDNIEBRUDyPQK - -When the 2014 POSA MOOC officially starts these videos will be -available at +These videos are available at https://class.coursera.org/posa-002/lecture @@ -72,134 +77,38 @@ We'll also discuss this assignment specification (and later its solution) in the POSA MOOC "Virtual Office Hours", which are described in item #38 at the POSA MOOC FAQ available from -http://www.courera.org/course/posa - -To compile this code you can either use the provided Eclipse project -or go into the src directory and simply type - -% javac FairnessChecker.java Palantir.java PalantirManager.java - PalantirManagerTest.java SimpleAtomicLong.java SimpleSemaphore.java - -on the command-line and then run the resulting class file by typing - -% java PalantirManagerTest - -The output for a correct solution should look something this: - ----------------------------------------- -Starting PalantirManagerTest -Pippen is acquiring the palantir -Pippen is starting to gaze at the MinisTirith palantir -Aragorn is acquiring the palantir -Aragorn is starting to gaze at the Orthanc palantir -Denathor is acquiring the palantir -Denathor is starting to gaze at the Barad-dur palantir -Sauron is acquiring the palantir -Saruman is acquiring the palantir -Pippen is finished gazing at the MinisTirith palantir -Pippen is releasing the MinisTirith palantir -Pippen is acquiring the palantir -Sauron is starting to gaze at the MinisTirith palantir -Sauron is finished gazing at the MinisTirith palantir -Sauron is releasing the MinisTirith palantir -Sauron is acquiring the palantir -Saruman is starting to gaze at the MinisTirith palantir -Saruman is finished gazing at the MinisTirith palantir -Pippen is starting to gaze at the MinisTirith palantir -Saruman is releasing the MinisTirith palantir -Saruman is acquiring the palantir -Pippen is finished gazing at the MinisTirith palantir -Pippen is releasing the MinisTirith palantir -Pippen is acquiring the palantir -Sauron is starting to gaze at the MinisTirith palantir -Sauron is finished gazing at the MinisTirith palantir -Sauron is releasing the MinisTirith palantir -Sauron is acquiring the palantir -Saruman is starting to gaze at the MinisTirith palantir -Saruman is finished gazing at the MinisTirith palantir -Pippen is starting to gaze at the MinisTirith palantir -Saruman is releasing the MinisTirith palantir -Saruman is acquiring the palantir -Pippen is finished gazing at the MinisTirith palantir -Pippen is releasing the MinisTirith palantir -Pippen is acquiring the palantir -Sauron is starting to gaze at the MinisTirith palantir -Sauron is finished gazing at the MinisTirith palantir -Sauron is releasing the MinisTirith palantir -Sauron is acquiring the palantir -Saruman is starting to gaze at the MinisTirith palantir -Saruman is finished gazing at the MinisTirith palantir -Pippen is starting to gaze at the MinisTirith palantir -Saruman is releasing the MinisTirith palantir -Saruman is acquiring the palantir -Denathor is finished gazing at the Barad-dur palantir -Denathor is releasing the Barad-dur palantir -Denathor is acquiring the palantir -Sauron is starting to gaze at the Barad-dur palantir -Pippen is finished gazing at the MinisTirith palantir -Pippen is releasing the MinisTirith palantir -Pippen is acquiring the palantir -Saruman is starting to gaze at the MinisTirith palantir -Saruman is finished gazing at the MinisTirith palantir -Denathor is starting to gaze at the MinisTirith palantir -Saruman is releasing the MinisTirith palantir -Saruman is acquiring the palantir -Denathor is finished gazing at the MinisTirith palantir -Denathor is releasing the MinisTirith palantir -Denathor is acquiring the palantir -Pippen is starting to gaze at the MinisTirith palantir -Pippen is finished gazing at the MinisTirith palantir -Pippen is releasing the MinisTirith palantir -Saruman is starting to gaze at the MinisTirith palantir -Saruman is finished gazing at the MinisTirith palantir -Denathor is starting to gaze at the MinisTirith palantir -Saruman is releasing the MinisTirith palantir -Aragorn is finished gazing at the Orthanc palantir -Aragorn is releasing the Orthanc palantir -Aragorn is acquiring the palantir -Aragorn is starting to gaze at the Orthanc palantir -Denathor is finished gazing at the MinisTirith palantir -Denathor is releasing the MinisTirith palantir -Denathor is acquiring the palantir -Denathor is starting to gaze at the MinisTirith palantir -Denathor is finished gazing at the MinisTirith palantir -Denathor is releasing the MinisTirith palantir -Denathor is acquiring the palantir -Denathor is starting to gaze at the MinisTirith palantir -Denathor is finished gazing at the MinisTirith palantir -Denathor is releasing the MinisTirith palantir -Sauron is finished gazing at the Barad-dur palantir -Sauron is releasing the Barad-dur palantir -Sauron is acquiring the palantir -Sauron is starting to gaze at the MinisTirith palantir -Sauron is finished gazing at the MinisTirith palantir -Sauron is releasing the MinisTirith palantir -Aragorn is finished gazing at the Orthanc palantir -Aragorn is releasing the Orthanc palantir -Aragorn is acquiring the palantir -Aragorn is starting to gaze at the MinisTirith palantir -Aragorn is finished gazing at the MinisTirith palantir -Aragorn is releasing the MinisTirith palantir -Aragorn is acquiring the palantir -Aragorn is starting to gaze at the MinisTirith palantir -Aragorn is finished gazing at the MinisTirith palantir -Aragorn is releasing the MinisTirith palantir -Aragorn is acquiring the palantir -Aragorn is starting to gaze at the MinisTirith palantir -Aragorn is finished gazing at the MinisTirith palantir -Aragorn is releasing the MinisTirith palantir -Finishing PalantirManagerTest ----------------------------------------- - -Don't worry if your output occurs in a different order - that's to be -expected in multi-threaded programs and OS/JVM platforms. Also, don't -worry about "warnings" since there's an intentional race condition in -the test since it's possible for one thread to call +http://www.coursera.org/course/posa + +To compile this code you need to use the provided Eclipse project. +There is a Unit Test Suite that will run all three Unit test files +included in the test/ directory for you at once. When you first open +the project in Eclipse, you might see compile errors if JUnit is not +included in your build path. To fix these errors, open +SynchronizedQueueTest.java, hover over "org.junit," and click "Fix +project setup." Make sure "Add JUnit 4 library to the build path" is +selected and then click "OK." At this point, the compile errors +should disappear! + +Right click on the test suite (AllTests.java) or an individual +*_UnitTest.java file in Eclipse and select 'Run As' -> 'JUnit +Test'. When the assignment is complete, all the tests should complete +successfully. If a test passes a green-check mark will appear next to +the test in the JUnit view. As long as this JUnit test "passes" +successfully your program will be be consider "correct" for the +purposes of assessing this assignment. + +If you'd like to enable verbose debugging output please set the +diagnosticsEnabled flag in PlanantirManagerUnitTest.java to true (it +defaults to false). However, this will generate a lot of output and +you'll need to look at it carefully to understand what it's doing. If +you do this, don't worry about "warnings" since there's an intentional +race condition in the test since it's possible for one thread to call mFairnessChecker.addNewThread() and then yield to another thread which again calls mFairnessChecker.addNewThread() and then goes on without interruption to call mPalantirManager.acquirePalantir(), which will fool the fairness checker into wrongly thinking the acquisition wasn't -fair. we'll just give a warning (rather than an error) if it looks -like the semaphore acquire() method isn't "fair". The key is not to -have runtime exceptions in your output! +fair. We therefore just give a warning (rather than an error) if it +looks like the semaphore acquire() method isn't "fair". The key is +not to have runtime exceptions, i.e., you want only "green-check +marks" in the JUnit view output! diff --git a/assignments/week-3-assignment-2/src/SimpleSemaphore.java b/assignments/week-3-assignment-2/src/SimpleSemaphore.java deleted file mode 100644 index fe3f65404..000000000 --- a/assignments/week-3-assignment-2/src/SimpleSemaphore.java +++ /dev/null @@ -1,62 +0,0 @@ -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.Condition; - -/** - * @class SimpleSemaphore - * - * @brief This class provides a simple counting semaphore - * implementation using Java a ReentrantLock and a - * ConditionObject. It must implement both "Fair" and - * "NonFair" semaphore semantics, just liked Java Semaphores. - */ -public class SimpleSemaphore { - /** - * Constructor initialize the data members. - */ - public SimpleSemaphore (int permits, - boolean fair) - { - // TODO - you fill in here - } - - /** - * Acquire one permit from the semaphore in a manner that can - * be interrupted. - */ - public void acquire() throws InterruptedException { - // TODO - you fill in here - } - - /** - * Acquire one permit from the semaphore in a manner that - * cannot be interrupted. - */ - public void acquireUninterruptibly() { - // TODO - you fill in here - } - - /** - * Return one permit to the semaphore. - */ - void release() { - // TODO - you fill in here - } - - /** - * Define a ReentrantLock to protect the critical section. - */ - // TODO - you fill in here - - /** - * Define a ConditionObject to wait while the number of - * permits is 0. - */ - // TODO - you fill in here - - /** - * Define a count of the number of available permits. - */ - // TODO - you fill in here -} - diff --git a/assignments/week-3-assignment-2/src/SimpleAtomicLong.java b/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java similarity index 58% rename from assignments/week-3-assignment-2/src/SimpleAtomicLong.java rename to assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java index 17dab3db1..f57064f95 100644 --- a/assignments/week-3-assignment-2/src/SimpleAtomicLong.java +++ b/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java @@ -1,5 +1,7 @@ // Import the necessary Java synchronization and scheduling classes. +package edu.vuum.mocca; + import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.Lock; @@ -16,32 +18,27 @@ class SimpleAtomicLong * The value that's manipulated atomically via the methods. */ private long mValue; - + + /** * The ReentrantReadWriteLock used to serialize access to mValue. */ - // TODO - replace the null with the appropriate initialization: - private ReentrantReadWriteLock mRWLock = null; + // TODO - add the implementation /** * Creates a new SimpleAtomicLong with the given initial value. */ - public SimpleAtomicLong(long initialValue) - { - long value = 0; + public SimpleAtomicLong(long initialValue) { // TODO - you fill in here } /** - * @brief Gets the current value. + * @brief Gets the current value * * @returns The current value */ - public long get() - { - long value = 0; - // TODO - you fill in here, using a readLock() - return value; + public long get() { + // TODO - you fill in here } /** @@ -49,11 +46,8 @@ public long get() * * @returns the updated value */ - public long decrementAndGet() - { - long value = 0; - // TODO - you fill in here, using a writeLock() - return value; + public long decrementAndGet() { + // TODO - you fill in here } /** @@ -61,11 +55,8 @@ public long decrementAndGet() * * @returns the previous value */ - public long getAndIncrement() - { - long value = 0; - // TODO - you fill in here, using a writeLock() - return value; + public long getAndIncrement() { + // TODO - you fill in here } /** @@ -73,11 +64,8 @@ public long getAndIncrement() * * @returns the previous value */ - public long getAndDecrement() - { - long value = 0; - // TODO - you fill in here, using a writeLock() - return value; + public long getAndDecrement() { + // TODO - you fill in here } /** @@ -85,11 +73,8 @@ public long getAndDecrement() * * @returns the updated value */ - public long incrementAndGet() - { - long value = 0; - // TODO - you fill in here, using a writeLock() - return value; + public long incrementAndGet() { + // TODO - you fill in here } } diff --git a/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java b/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java new file mode 100644 index 000000000..a867818ed --- /dev/null +++ b/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java @@ -0,0 +1,70 @@ +package edu.vuum.mocca; + +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.Condition; + +/** + * @class SimpleSemaphore + * + * @brief This class provides a simple counting semaphore + * implementation using Java a ReentrantLock and a + * ConditionObject (which is accessed via a Condition). It must + * implement both "Fair" and "NonFair" semaphore semantics, + * just liked Java Semaphores. + */ +public class SimpleSemaphore { + /** + * Define a Lock to protect the critical section. + */ + // TODO - you fill in here + + /** + * Define a Condition that waits while the number of permits is 0. + */ + // TODO - you fill in here + + /** + * Define a count of the number of available permits. + */ + // TODO - you fill in here. Make sure that this data member will + // ensure its values aren't cached by multiple Threads.. + + public SimpleSemaphore(int permits, boolean fair) { + // TODO - you fill in here to initialize the SimpleSemaphore, + // making sure to allow both fair and non-fair Semaphore + // semantics. + } + + /** + * Acquire one permit from the semaphore in a manner that can be + * interrupted. + */ + public void acquire() throws InterruptedException { + // TODO - you fill in here. + } + + /** + * Acquire one permit from the semaphore in a manner that cannot be + * interrupted. + */ + public void acquireUninterruptibly() { + // TODO - you fill in here. + } + + /** + * Return one permit to the semaphore. + */ + public void release() { + // TODO - you fill in here. + } + + /** + * Return the number of permits available. + */ + public int availablePermits() { + // TODO - you fill in here by changing null to the appropriate + // return value. + return null; + } +} diff --git a/assignments/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java new file mode 100644 index 000000000..325a23f41 --- /dev/null +++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java @@ -0,0 +1,12 @@ +package edu.vuum.mocca; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({PalantirManagerUnitTest.class, + SimpleAtomicLongUnitTest.class, + SimpleSemaphoreUnitTest.class}) +public class AllTests { +} diff --git a/assignments/week-3-assignment-2/src/FairnessChecker.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java similarity index 68% rename from assignments/week-3-assignment-2/src/FairnessChecker.java rename to assignments/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java index cf688c746..2d8e1ff6a 100644 --- a/assignments/week-3-assignment-2/src/FairnessChecker.java +++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java @@ -1,3 +1,5 @@ +package edu.vuum.mocca; + import java.util.List; import java.util.ArrayList; @@ -5,7 +7,10 @@ * @class FairnessChecker * * @brief Class that attempts to check whether the SimpleSemaphore - * implementation is "fair". + * implementation is "fair". It has inherent limitations, but + * to fix these limitations would require obtrusive + * instrumentation within the SimpleSemaphore implementation + * itself. */ public class FairnessChecker { /** @@ -17,7 +22,7 @@ public class FairnessChecker { /** * Initialize the FairnessChecker */ - public FairnessChecker(int totalEntries) { + public FairnessChecker(final int totalEntries) { mEntryList = new ArrayList(totalEntries); } @@ -25,7 +30,7 @@ public FairnessChecker(int totalEntries) { * Add the name of a Thread that's about to acquire the @code * SimpleSemaphore. Assumes that Thread name are unique. */ - public synchronized void addNewThread(String entry) { + public synchronized void addNewThread(final String entry) { mEntryList.add(entry); } @@ -33,7 +38,7 @@ public synchronized void addNewThread(String entry) { * Returns true if the calling Thread's name is the same as the * first item in the list, else false. */ - public synchronized boolean checkOrder(String entry) { + public synchronized boolean checkOrder(final String entry) { String currentEntry = mEntryList.remove(0); return currentEntry.equals(entry); diff --git a/assignments/week-3-assignment-2/src/Palantir.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java similarity index 93% rename from assignments/week-3-assignment-2/src/Palantir.java rename to assignments/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java index 023abad56..1ecd8d2bd 100644 --- a/assignments/week-3-assignment-2/src/Palantir.java +++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java @@ -1,3 +1,4 @@ +package edu.vuum.mocca; /** * @class Palantir * @@ -5,6 +6,7 @@ * Essentially plays the role of a "command" in the Command * pattern. */ + interface Palantir { /** * Gaze into the Palantir (and go into a tranc ;-)). diff --git a/assignments/week-3-assignment-2/src/PalantirManager.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java similarity index 92% rename from assignments/week-3-assignment-2/src/PalantirManager.java rename to assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java index 5f7486f45..dedf2454a 100644 --- a/assignments/week-3-assignment-2/src/PalantirManager.java +++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java @@ -1,3 +1,5 @@ +package edu.vuum.mocca; + import java.util.List; /** @@ -34,7 +36,7 @@ public class PalantirManager { * Create a resource manager for the palantiri passed as a * parameter. */ - PalantirManager(List palantiri) { + PalantirManager(final List palantiri) { mMaxPalantiri = palantiri.size(); mPalantiri = palantiri; used = new boolean[palantiri.size()]; @@ -58,7 +60,7 @@ public Palantir acquirePalantir() { * Returns the designated @code palantir so that it's * available for others to use. */ - public void releasePalantir(Palantir palantir) { + public void releasePalantir(final Palantir palantir) { if (markAsUnused(palantir)) mAvailable.release(); } @@ -81,7 +83,7 @@ protected synchronized Palantir getNextAvailablePalantir() { /** * Return the @code palantir back to the resource pool. */ - protected synchronized boolean markAsUnused(Palantir palantir) { + protected synchronized boolean markAsUnused(final Palantir palantir) { // Linear search is fine for this simple demo. for (int i = 0; i < mMaxPalantiri; ++i) { if (palantir == mPalantiri.get(i)) { diff --git a/assignments/week-3-assignment-2/src/PalantirManagerTest.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java similarity index 52% rename from assignments/week-3-assignment-2/src/PalantirManagerTest.java rename to assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java index f4e5c485c..90edc858f 100644 --- a/assignments/week-3-assignment-2/src/PalantirManagerTest.java +++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java @@ -1,28 +1,51 @@ +package edu.vuum.mocca; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertFalse; + +import java.util.ArrayList; import java.util.List; import java.util.ListIterator; -import java.util.ArrayList; + +import org.junit.Test; /** - * @class PalantirManagerTest + * @class PalantirManagerUnitTest * - * @brief This program tests student implementations of - * SimpleSimpleAtomicLong and SimpleSemaphore, which are used - * to correctly implement a resource manager that - * limits the number of Beings from Middle-Earth who - * can concurrently gaze into a Palantir (if you're not a Lord of the - * Ring's fan see http://en.wikipedia.org/wiki/Palantir for more - * information on Palantir). + * @brief This program tests that student implementations of + * SimpleAtomicLong and SimpleSemaphore correctly implement a + * resource manager that limits the number of Beings from + * Middle-Earth who can concurrently gaze into a Palantir (see + * http://en.wikipedia.org/wiki/Palantir for more information + * on Palantirs if you're not a Lord of the Ring's fan yet + * ;-)). */ -class PalantirManagerTest -{ +public class PalantirManagerUnitTest { + /** + * If this is set to true in then lots of debugging output will be + * generated. + */ + public static boolean diagnosticsEnabled = false; + /** * Total number of times each Palantir user gets to gaze into a * Palantir. */ final static int mMaxPalantirSessions = 5; + /** + * Total number of active Threads. + */ static volatile long mMaxActiveThreads = 0; + /** + * Keep track of whether a runtime exception occurs. + */ + boolean mFailed = false; + + /** + * Count of the number of Active Threads. + */ static SimpleAtomicLong mActiveThreads = new SimpleAtomicLong(0); /** @@ -36,8 +59,10 @@ class PalantirManagerTest * implementation is "fair". */ static FairnessChecker mFairnessChecker = null; - - /* Runnable passed to each Thread that uses a Palantir. */ + + /** + * Runnable passed to each Thread that uses a Palantir. + */ static Runnable usePalantir = new Runnable() { /** * This is the main loop run by each Being of Middle-Earth @@ -47,8 +72,9 @@ public void run() { // Bound the total number of times that each user can // gaze into a Palantir. for (int i = 0; i < mMaxPalantirSessions; ++i) { - System.out.println(Thread.currentThread().getName() - + " is acquiring the palantir"); + if (diagnosticsEnabled) + System.out.println(Thread.currentThread().getName() + + " is acquiring the palantir"); // Used to check for Semaphore fairness. mFairnessChecker.addNewThread(Thread.currentThread().getName()); @@ -68,24 +94,27 @@ public void run() { // the acquisition wasn't fair. we'll just give a // warning (rather than an error) if it looks like // the semaphore acquire() method isn't "fair". - if (!mFairnessChecker.checkOrder(Thread.currentThread().getName())) - System.out.println(Thread.currentThread().getName() - + ": warning, semaphore acquire may not be fair"); + if (!mFairnessChecker.checkOrder(Thread.currentThread() + .getName())) { + if (diagnosticsEnabled) + System.out.println(Thread.currentThread().getName() + + ": warning, semaphore acquire may not be fair"); + } // Ensure that the Semaphore implementation is // correctly limiting the number of Palantir // gazers. long activeThreads = mActiveThreads.getAndIncrement(); - if (mMaxActiveThreads < activeThreads) - { - System.out.println("too many threads = " - + activeThreads); - throw new RuntimeException(); - } - System.out.println(Thread.currentThread().getName() - + " is starting to gaze at the " - + palantir.name() - + " palantir"); + if (mMaxActiveThreads < activeThreads) { + System.out.println("too many threads = " + + activeThreads); + throw new RuntimeException(); + } + if (diagnosticsEnabled) + System.out.println(Thread.currentThread().getName() + + " is starting to gaze at the " + + palantir.name() + + " palantir"); // Gaze at the Palantir for the time alloted in // the command. @@ -95,23 +124,25 @@ public void run() { // Palantir. mActiveThreads.decrementAndGet(); - System.out.println(Thread.currentThread().getName() - + " is finished gazing at the " - + palantir.name() - + " palantir"); + if (diagnosticsEnabled) + System.out.println(Thread.currentThread().getName() + + " is finished gazing at the " + + palantir.name() + + " palantir"); // Return the Palantir back to the shared pool so // other Beings can gaze at it. mPalantirManager.releasePalantir(palantir); - System.out.println(Thread.currentThread().getName() - + " is releasing the " - + palantir.name() + - " palantir"); + if (diagnosticsEnabled) + System.out.println(Thread.currentThread().getName() + + " has released the " + + palantir.name() + + " palantir"); } - + } - }; + }; /** * This factory creates a list of Palantiri. @@ -120,51 +151,57 @@ static List makePalantiri() { List palantiri = new ArrayList(); // MinasTirith Palantir - palantiri.add (new Palantir() { - public void gaze() { + palantiri.add(new Palantir() { + public void gaze() { try { - Thread.sleep(10); + Thread.sleep(10); + } catch (InterruptedException e) { } - catch (InterruptedException e) {} } - public String name() {return "MinasTirith";} + public String name() { + return "MinasTirith"; + } }); // Orthanc Palantir - palantiri.add (new Palantir() { + palantiri.add(new Palantir() { public void gaze() { try { Thread.sleep(150); + } catch (InterruptedException e) { } - catch (InterruptedException e) {} } - public String name() {return "Orthanc";} + public String name() { + return "Orthanc"; + } }); // Barad-dur Palantir - palantiri.add (new Palantir() { - public void gaze() - { + palantiri.add(new Palantir() { + public void gaze() { try { // The unblinking eye gazes for a long time // ;-) Thread.sleep(100); + } catch (InterruptedException e) { } - catch (InterruptedException e) {} } - public String name() {return "Barad-dur";} + public String name() { + return "Barad-dur"; + } }); return palantiri; } - /** - * Main entry point method that runs the test. - */ - public static void main(String[] args) { + + @Test + public void testPalantirManager() { try { - System.out.println("Starting PalantirManagerTest"); + if (diagnosticsEnabled) + System.out.println("Starting PalantirManagerTest"); // Get the list of available Palantiri. - List palantiri = makePalantiri(); + List palantiri = + PalantirManagerUnitTest.makePalantiri(); // Limit the number of users (threads) that can gaze into // the available Palantiri. @@ -189,23 +226,46 @@ public static void main(String[] args) { // Start all the Threads that Middle-Earth Beings use to // gaze into the Palantir. - for (ListIterator iterator = palantirUsers.listIterator(); + for (ListIterator iterator = palantirUsers.listIterator(); iterator.hasNext(); - ) - iterator.next().start(); + ) { + Thread t = iterator.next(); + // Catch runtime exceptions and induce a JUnit test + // failure. + t.setUncaughtExceptionHandler + (new Thread.UncaughtExceptionHandler() { + public void uncaughtException(Thread t, + Throwable e) { + System.out.println(t + + " throws exception: " + + e); + mFailed = true; + } + }); + t.start(); + } // Barrier synchronization that waits for all the Threads // to exit. - for (ListIterator iterator = palantirUsers.listIterator(); + for (ListIterator iterator = palantirUsers.listIterator(); iterator.hasNext(); ) iterator.next().join(); - System.out.println("Finishing PalantirManagerTest"); - } - catch (Exception e) { - System.out.println("Something went wrong"); + // Make sure we haven't failed. + assertFalse(mFailed); + + if (diagnosticsEnabled) + System.out.println("Finishing PalantirManagerTest"); + } catch (Exception e) { + if (diagnosticsEnabled) + System.out.println("A " + + e.getMessage() + + " Exception was thrown"); + fail("A " + + e.getMessage() + + " Exception was thrown"); } } - + } diff --git a/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java new file mode 100644 index 000000000..69895b8ac --- /dev/null +++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java @@ -0,0 +1,93 @@ +package edu.vuum.mocca; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +/** + * @class SimpleAtomicLongUnitTest + * + * @brief Simple unit test for the SimpleAtomicLong clas that ensures + * the version submitted for this assignment works correctly. + */ +public class SimpleAtomicLongUnitTest { + @Test + public void testSimpleAtomicLong() { + SimpleAtomicLong testLong = new SimpleAtomicLong(0); + assertNotNull(testLong); + } + + @Test + public void testGet() { + SimpleAtomicLong testLong = new SimpleAtomicLong(0); + assertEquals(testLong.get(), 0); + + SimpleAtomicLong testLong2 = new SimpleAtomicLong(100); + assertEquals(testLong2.get(), 100); + + SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100); + assertEquals(testLong3.get(), -100); + } + + @Test + public void testDecrementAndGet() { + SimpleAtomicLong testLong = new SimpleAtomicLong(0); + assertEquals(testLong.decrementAndGet(), -1); + assertEquals(testLong.get(), -1); + + SimpleAtomicLong testLong2 = new SimpleAtomicLong(100); + assertEquals(testLong2.decrementAndGet(), 99); + assertEquals(testLong2.get(), 99); + + SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100); + assertEquals(testLong3.decrementAndGet(), -101); + assertEquals(testLong3.get(), -101); + } + + @Test + public void testIncrementAndGet() { + SimpleAtomicLong testLong = new SimpleAtomicLong(0); + assertEquals(testLong.incrementAndGet(), 1); + assertEquals(testLong.get(), 1); + + SimpleAtomicLong testLong2 = new SimpleAtomicLong(100); + assertEquals(testLong2.incrementAndGet(), 101); + assertEquals(testLong2.get(), 101); + + SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100); + assertEquals(testLong3.incrementAndGet(), -99); + assertEquals(testLong3.get(), -99); + } + + @Test + public void testGetAndIncrement() { + SimpleAtomicLong testLong = new SimpleAtomicLong(0); + assertEquals(testLong.getAndIncrement(), 0); + assertEquals(testLong.get(), 1); + + SimpleAtomicLong testLong2 = new SimpleAtomicLong(100); + assertEquals(testLong2.getAndIncrement(), 100); + assertEquals(testLong2.get(), 101); + + SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100); + assertEquals(testLong3.getAndIncrement(), -100); + assertEquals(testLong3.get(), -99); + } + + @Test + public void testGetAndDecrement() { + SimpleAtomicLong testLong = new SimpleAtomicLong(0); + assertEquals(testLong.getAndDecrement(), 0); + assertEquals(testLong.get(), -1); + + SimpleAtomicLong testLong2 = new SimpleAtomicLong(100); + assertEquals(testLong2.getAndDecrement(), 100); + assertEquals(testLong2.get(), 99); + + SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100); + assertEquals(testLong3.getAndDecrement(), -100); + assertEquals(testLong3.get(), -101); + } + +} diff --git a/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java new file mode 100644 index 000000000..8cdd59390 --- /dev/null +++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java @@ -0,0 +1,68 @@ +package edu.vuum.mocca; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.concurrent.Semaphore; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * @class SimpleSemaphoreUnitTest + * + * @brief Simple unit test for the SimpleSemaphore that just tests + * single-threaded logic. + */ +public class SimpleSemaphoreUnitTest { + @Test + public void testSimpleSemaphore() { + SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true); + assertNotNull(simpleSemaphore); + } + + @Test + public void testAcquire() throws InterruptedException { + SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true); + assertEquals(simpleSemaphore.availablePermits(), 2); + simpleSemaphore.acquire(); + assertEquals(simpleSemaphore.availablePermits(), 1); + simpleSemaphore.acquire(); + assertEquals(simpleSemaphore.availablePermits(), 0); + } + + @Test + public void testAcquireUninterruptibly() throws InterruptedException { + SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true); + assertEquals(simpleSemaphore.availablePermits(), 2); + simpleSemaphore.acquireUninterruptibly(); + assertEquals(simpleSemaphore.availablePermits(), 1); + simpleSemaphore.acquireUninterruptibly(); + assertEquals(simpleSemaphore.availablePermits(), 0); + } + + @Test + public void testRelease() throws InterruptedException { + SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true); + assertEquals(simpleSemaphore.availablePermits(), 2); + simpleSemaphore.acquire(); + assertEquals(simpleSemaphore.availablePermits(), 1); + simpleSemaphore.acquire(); + assertEquals(simpleSemaphore.availablePermits(), 0); + simpleSemaphore.release(); + assertEquals(simpleSemaphore.availablePermits(), 1); + simpleSemaphore.release(); + assertEquals(simpleSemaphore.availablePermits(), 2); + } + + @Test + public void testAvailablePermits() throws InterruptedException{ + SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true); + assertEquals(simpleSemaphore.availablePermits(), 2); + simpleSemaphore.acquire(); + assertEquals(simpleSemaphore.availablePermits(), 1); + } +} diff --git a/assignments/week-4-assignment-3/.classpath b/assignments/week-4-assignment-3/.classpath index 18d70f02c..b0e3db7ba 100644 --- a/assignments/week-4-assignment-3/.classpath +++ b/assignments/week-4-assignment-3/.classpath @@ -1,6 +1,8 @@ + + diff --git a/assignments/week-4-assignment-3/Assignment-Description.txt b/assignments/week-4-assignment-3/Assignment-Description.txt index c6452c2ed..66cb926d8 100644 --- a/assignments/week-4-assignment-3/Assignment-Description.txt +++ b/assignments/week-4-assignment-3/Assignment-Description.txt @@ -1,11 +1,15 @@ -Programming Assignment 1 +Week 4: Programming Assignment 3 + +Released Monday, June 2nd, 2014 +Due Monday, June 16th, 2014 In this assignment, you will implement a Java program that creates two instances of the PlayPingPongThread and then starts these thread instances to correctly alternate printing "Ping" and "Pong", respectively, on the console display. This assignment also reuses the -SimpleSemaphore you implemented for the previous assignment, so make -sure it's working properly before attempting this assignment! +SimpleSemaphore you implemented for the previous assignment, so please +apply any fixes motivated by watching the Virtual Office Hours video +of the instructor's solution(s). In this directory you'll find the PingPongRight.java class, which contains the skeleton Java code that you'll implement by completing @@ -16,7 +20,9 @@ DO NOT CHANGE THE OVERALL STRUCTURE OF THE SKELETON - just fill in the In particular, you'll need to do the following: . Implement the SimpleSemaphore class, which you should replace with - your working solution from the previous assignment. + your working solution from the previous assignment, after applying + any fixes motivated by watching the Virtual Office Hours video of + the instructor's solution(s). . Use your SimpleSemaphore to implement the PlayPingPongThread class and fix the problems with the earlier PingPongWrong solution, which @@ -43,13 +49,19 @@ In particular, you'll need to do the following: Section 1: Module 2: Part 1: Overview of Java Threads (Part 1) Section 1: Module 2: Part 2: Overview of Java Threads (Part 2) -Note, at the moment, all these videos are available in the YouTube -playlist at - -https://www.youtube.com/playlist?list=PLZ9NgFYEMxp4tbiFYip6tDNIEBRUDyPQK +. A more sophisticated example of a concurrent ping-pong program is + is shown in these videos: -When the 2014 POSA MOOC officially starts these videos will be -available at + Section 1: Module 2: Part 10: Java Synchronization and Scheduling Example (Part 1) + Section 1: Module 2: Part 11: Java Synchronization and Scheduling Example (Part 2) + + This programming assignment isn't as sophisticated (e.g., it doesn't + implement a framework and doesn't use as many GoF patterns), but I + suggest you watch these videos to learn more about how binary + semaphores can be used to correctly alternate printing "ping" and + "pong" from two Java Threads. + +These videos are available at https://class.coursera.org/posa-002/lecture @@ -60,7 +72,7 @@ in item #38 at the POSA MOOC FAQ available from http://www.courera.org/course/posa To compile this code you can either use the provided Eclipse project -or go into the src directory and simply type +or go into the src/edu/vuum/mooca directory and simply type % javac PingPongRight.java SimpleSemaphore.java @@ -68,33 +80,45 @@ and to run the resulting class file simply type % java PingPongRight -The correct solution should look exactly like this: +The correct solution when you run PingPongRight should look exactly +like this: Ready...Set...Go! -Ping!(1) -Pong!(1) -Ping!(2) -Pong!(2) -Ping!(3) -Pong!(3) -Ping!(4) -Pong!(4) -Ping!(5) -Pong!(5) -Ping!(6) -Pong!(6) -Ping!(7) -Pong!(7) -Ping!(8) -Pong!(8) -Ping!(9) -Pong!(9) -Ping!(10) -Pong!(10) +Ping! (1) + Pong! (1) +Ping! (2) + Pong! (2) +Ping! (3) + Pong! (3) +Ping! (4) + Pong! (4) +Ping! (5) + Pong! (5) +Ping! (6) + Pong! (6) +Ping! (7) + Pong! (7) +Ping! (8) + Pong! (8) +Ping! (9) + Pong! (9) +Ping! (10) + Pong! (10) Done! - - - - +There is also a Unit Test Suite that will run the PingPongRightTest +file included in the test/edu/vuum/mooca directory for you at. When +you first open the project in Eclipse, you might see compile errors if +JUnit is not included in your build path. To fix these errors, open +SynchronizedQueueTest.java, hover over "org.junit," and click "Fix +project setup." Make sure "Add JUnit 4 library to the build path" is +selected and then click "OK." At this point, the compile errors +should disappear! + +Right click on the PingPongRightTest.java file in Eclipse and select +'Run As' -> 'JUnit Test'. When the assignment is complete, the test +should complete successfully. If the test passes a green-check mark +will appear next to the test in the JUnit view. As long as this JUnit +test "passes" successfully your program will be be consider "correct" +for the purposes of assessing this assignment. diff --git a/assignments/week-4-assignment-3/src/PingPongRight.java b/assignments/week-4-assignment-3/src/PingPongRight.java deleted file mode 100644 index 53b8411d4..000000000 --- a/assignments/week-4-assignment-3/src/PingPongRight.java +++ /dev/null @@ -1,100 +0,0 @@ -// Import the necessary Java synchronization and scheduling classes. - -import java.util.concurrent.CountDownLatch; - -/** - * @class PingPongRight - * - * @brief This class implements a Java program that creates two - * instances of the PlayPingPongThread and start these thread - * instances to correctly alternate printing "Ping" and "Pong", - * respectively, on the console display. - */ -public class PingPongRight { - /** - * Number of iterations to run the test program. - */ - public static int mMaxIterations = 10; - - /** - * Latch that will be decremented each time a thread exits. - */ - public static CountDownLatch latch = null; // TODO - You fill in here - - /** - * @class PlayPingPongThread - * - * @brief This class implements the ping/pong processing algorithm - * using the SimpleSemaphore to alternate printing "ping" - * and "pong" to the console display. - */ - public static class PlayPingPongThread extends Thread - { - /** - * Constructor initializes the data member. - */ - public PlayPingPongThread (/* TODO - You fill in here */) - { - // TODO - You fill in here. - } - - /** - * Main event loop that runs in a separate thread of control - * and performs the ping/pong algorithm using the - * SimpleSemaphores. - */ - public void run () - { - // TODO - You fill in here. - } - - /** - * String to print (either "ping!" or "pong"!) for each - * iteration. - */ - // TODO - You fill in here. - - /** - * The two SimpleSemaphores use to alternate pings and pongs. - */ - // TODO - You fill in here. - } - - /** - * The main() entry point method into PingPongRight program. - */ - public static void main(String[] args) { - try { - // Create the ping and pong SimpleSemaphores that control - // alternation between threads. - - // TODO - You fill in here. - - System.out.println("Ready...Set...Go!"); - - // Create the ping and pong threads, passing in the string - // to print and the appropriate SimpleSemaphores. - PlayPingPongThread ping = - new PlayPingPongThread(/* TODO - You fill in here */); - PlayPingPongThread pong = - new PlayPingPongThread(/* TODO - You fill in here */); - - // Initiate the ping and pong threads, which will call the - // run() hook method. - ping.start(); - pong.start(); - - // Use barrier synchronization to wait for both threads to - // finish. - - // TODO - replace replace the following line with a - // CountDownLatch barrier synchronizer call that waits for - // both threads to finish. - throw new java.lang.InterruptedException(); - } - catch (java.lang.InterruptedException e) - {} - - System.out.println("Done!"); - } -} diff --git a/assignments/week-4-assignment-3/src/SimpleSemaphore.java b/assignments/week-4-assignment-3/src/SimpleSemaphore.java deleted file mode 100644 index d3c534373..000000000 --- a/assignments/week-4-assignment-3/src/SimpleSemaphore.java +++ /dev/null @@ -1,61 +0,0 @@ -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.Condition; - -/** - * @class SimpleSemaphore - * - * @brief This class provides a simple counting semaphore - * implementation using Java a ReentrantLock and a - * ConditionObject. It must implement both "Fair" and - * "NonFair" semaphore semantics, just liked Java Semaphores. - */ -public class SimpleSemaphore { - /** - * Constructor initialize the data members. - */ - public SimpleSemaphore (int permits, - boolean fair) - { - // TODO - you fill in here - } - - /** - * Acquire one permit from the semaphore in a manner that can - * be interrupted. - */ - public void acquire() throws InterruptedException { - // TODO - you fill in here - } - - /** - * Acquire one permit from the semaphore in a manner that - * cannot be interrupted. - */ - public void acquireUninterruptibly() { - // TODO - you fill in here - } - - /** - * Return one permit to the semaphore. - */ - void release() { - // TODO - you fill in here - } - - /** - * Define a ReentrantLock to protect the critical section. - */ - // TODO - you fill in here - - /** - * Define a ConditionObject to wait while the number of - * permits is 0. - */ - // TODO - you fill in here - - /** - * Define a count of the number of available permits. - */ - // TODO - you fill in here -} diff --git a/assignments/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java b/assignments/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java new file mode 100644 index 000000000..4fd24024f --- /dev/null +++ b/assignments/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java @@ -0,0 +1,157 @@ +package edu.vuum.mocca; + +// Import the necessary Java synchronization and scheduling classes. +import java.util.concurrent.CountDownLatch; + +/** + * @class PingPongRight + * + * @brief This class implements a Java program that creates two + * instances of the PlayPingPongThread and start these thread + * instances to correctly alternate printing "Ping" and "Pong", + * respectively, on the console display. + */ +public class PingPongRight { + /** + * Number of iterations to run the test program. + */ + public final static int mMaxIterations = 10; + + /** + * Latch that will be decremented each time a thread exits. + */ + public static CountDownLatch mLatch = null; + + /** + * @class PlayPingPongThread + * + * @brief This class implements the ping/pong processing algorithm + * using the SimpleSemaphore to alternate printing "ping" + * and "pong" to the console display. + */ + public static class PlayPingPongThread extends Thread { + /** + * Constants to distinguish between ping and pong + * SimpleSemaphores, if you choose to use an array of + * SimpleSemaphores. If you don't use this implementation + * feel free to remove these constants. + */ + private final static int FIRST_SEMA = 0; + private final static int SECOND_SEMA = 1; + + /** + * Maximum number of loop iterations. + */ + private int mMaxLoopIterations = 0; + + /** + * String to print (either "ping!" or "pong"!) for each + * iteration. + */ + // TODO - You fill in here. + + /** + * Two SimpleSemaphores use to alternate pings and pongs. You + * can use an array of SimpleSemaphores or just define them as + * two data members. + */ + // TODO - You fill in here. + + /** + * Constructor initializes the data member(s). + */ + public PlayPingPongThread(String stringToPrint, + SimpleSemaphore semaphoreOne, + SimpleSemaphore semaphoreTwo, + int maxIterations) { + // TODO - You fill in here. + } + + /** + * Main event loop that runs in a separate thread of control + * and performs the ping/pong algorithm using the + * SimpleSemaphores. + */ + public void run() { + /** + * This method runs in a separate thread of control and + * implements the core ping/pong algorithm. + */ + + // TODO - You fill in here. + } + + /** + * Method for acquiring the appropriate SimpleSemaphore. + */ + private void acquire() { + // TODO fill in here + } + + /** + * Method for releasing the appropriate SimpleSemaphore. + */ + private void release() { + // TODO fill in here + } + } + + /** + * The method that actually runs the ping/pong program. + */ + public static void process(String startString, + String pingString, + String pongString, + String finishString, + int maxIterations) throws InterruptedException { + + // TODO initialize this by replacing null with the appropriate + // constructor call. + mLatch = null; + + // Create the ping and pong SimpleSemaphores that control + // alternation between threads. + + // TODO - You fill in here, make pingSema start out unlocked. + SimpleSemaphore pingSema = null; + // TODO - You fill in here, make pongSema start out locked. + SimpleSemaphore pongSema = null; + + System.out.println(startString); + + // Create the ping and pong threads, passing in the string to + // print and the appropriate SimpleSemaphores. + PlayPingPongThread ping = new PlayPingPongThread(/* + * TODO - You fill in + * here + */); + PlayPingPongThread pong = new PlayPingPongThread(/* + * TODO - You fill in + * here + */); + + // TODO - Initiate the ping and pong threads, which will call + // the run() hook method. + + // TODO - replace the following line with a barrier + // synchronizer call to mLatch that waits for both threads to + // finish. + throw new java.lang.InterruptedException(); + + System.out.println(finishString); + } + + /** + * The main() entry point method into PingPongRight program. + * + * @throws InterruptedException + */ + public static void main(String[] args) throws InterruptedException { + process("Ready...Set...Go!", + "Ping! ", + " Pong! ", + "Done!", + mMaxIterations); + } +} + diff --git a/assignments/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java b/assignments/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java new file mode 100644 index 000000000..5e473adf6 --- /dev/null +++ b/assignments/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java @@ -0,0 +1,66 @@ +package edu.vuum.mocca; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @class SimpleSemaphore + * + * @brief This class provides a simple counting semaphore implementation using + * Java a ReentrantLock and a ConditionObject (which is accessed via a + * Condition). It must implement both "Fair" and "NonFair" semaphore + * semantics, just liked Java Semaphores. + */ +public class SimpleSemaphore { + /** + * Define a ReentrantLock to protect the critical section. + */ + // TODO - you fill in here + + /** + * Define a Condition that waits while the number of permits is 0. + */ + // TODO - you fill in here + + /** + * Define a count of the number of available permits. + */ + // TODO - you fill in here. Make sure that this data member will + // ensure its values aren't cached by multiple Threads.. + + public SimpleSemaphore(int permits, boolean fair) { + // TODO - you fill in here to initialize the SimpleSemaphore, + // making sure to allow both fair and non-fair Semaphore + // semantics. + } + + /** + * Acquire one permit from the semaphore in a manner that can be + * interrupted. + */ + public void acquire() throws InterruptedException { + // TODO - you fill in here. + } + + /** + * Acquire one permit from the semaphore in a manner that cannot be + * interrupted. + */ + public void acquireUninterruptibly() { + // TODO - you fill in here. + } + + /** + * Return one permit to the semaphore. + */ + void release() { + // TODO - you fill in here. + } + + /** + * Return the number of permits available. + */ + public int availablePermits() { + // TODO - you fill in here to return the correct result + return 0; + } +} diff --git a/assignments/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java b/assignments/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java new file mode 100644 index 000000000..5148fba59 --- /dev/null +++ b/assignments/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java @@ -0,0 +1,100 @@ +package edu.vuum.mocca; + +import static org.junit.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @class PingPongRightTest + * + * @brief This JUnit test checks the PingPong program to make sure it's working + * properly. + */ +public class PingPongRightTest { + /* + * These data members are used to capture [ + * System.out.println() ] for testing. + */ + private final ByteArrayOutputStream outContent = + new ByteArrayOutputStream(); + private final ByteArrayOutputStream errContent = + new ByteArrayOutputStream(); + + /* + * Setup the output Capturing. + */ + @Before + public void setUpStreams() { + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + /* + * Tear-down the output Capturing. + */ + @After + public void cleanUpStreams() { + System.setOut(null); + System.setErr(null); + } + + /* + * Test the process(...) logic/accuracy + * + * Gives some helpful error outputs for some simple error states. + */ + @Test(timeout = 3000) + // This test will only run for 3000ms => 3 seconds, otherwise if fails + public void test() throws InterruptedException, IOException { + for (int i = 0; i < 10; i++) { + PingPongRight.process("start", "a", "b", "end", 10); + String outResults = outContent.toString(); + + if (outResults == null || outResults.length() == 0) { + fail("No output at all."); + } + + //We need to split things up for Windows and UNIX due to + //differences in the way that newlines are handled. + boolean windowsTrue = outResults.equals(testResultWindows); + boolean unixTrue = outResults.equals(testResultUnix); + boolean pingAllFirstTrue = outResults.equals(pingAllFirst); + boolean pongAllFirstTrue = outResults.equals(pongAllFirst); + + if (errContent.toString().length() != 0) + fail("There was error text."); + + if (pingAllFirstTrue) + fail("Ping Thread completed before Pong started."); + + if (pongAllFirstTrue) + fail("Pong Thread completed before Ping started."); + + if (!(windowsTrue || unixTrue)) + fail("Output was wrong.\n" + + "--- Received output ---\n" + + outResults + + "--- Expected output ---\n" + + testResultWindows); + + outContent.reset(); + } + } + + // This is what should be output \n was replaced for visible + // endlines for inclusion into single line. + String testResultUnix = + "start\na(1)\nb(1)\na(2)\nb(2)\na(3)\nb(3)\na(4)\nb(4)\na(5)\nb(5)\na(6)\nb(6)\na(7)\nb(7)\na(8)\nb(8)\na(9)\nb(9)\na(10)\nb(10)\nend\n"; + String testResultWindows = + "start\r\na(1)\r\nb(1)\r\na(2)\r\nb(2)\r\na(3)\r\nb(3)\r\na(4)\r\nb(4)\r\na(5)\r\nb(5)\r\na(6)\r\nb(6)\r\na(7)\r\nb(7)\r\na(8)\r\nb(8)\r\na(9)\r\nb(9)\r\na(10)\r\nb(10)\r\nend\r\n"; + String pingAllFirst = + "start\r\na(1)\r\nb(1)\r\na(2)\r\na(3)\r\na(4)\r\na(5)\r\na(6)\r\na(7)\r\na(8)\r\na(9)\r\na(10)\r\nb(2)\r\nb(3)\r\nb(4)\r\nb(5)\r\nb(6)\r\nb(8)\r\nb(7)\r\nb(9)\r\nb(10)\r\nend\r\n"; + String pongAllFirst = + "start\r\nb(1)\r\nb(2)\r\nb(3)\r\nb(4)\r\nb(5)\r\nb(6)\r\nb(8)\r\nb(7)\r\nb(9)\r\nb(10)\r\na(1)\r\na(2)\r\na(3)\r\na(4)\r\na(5)\r\na(6)\r\na(7)\r\na(8)\r\na(9)\r\na(10)\r\nend\r\n"; +} diff --git a/assignments/week-5-assignment-4/Assignment-Description.txt b/assignments/week-5-assignment-4/Assignment-Description.txt index cad036f73..066243159 100644 --- a/assignments/week-5-assignment-4/Assignment-Description.txt +++ b/assignments/week-5-assignment-4/Assignment-Description.txt @@ -1,21 +1,28 @@ -Programming Assignment 4 +Week 5: Programming Assignment 4 + +Released Monday, June 9th, 2014 +Due Monday, June 23rd, 2014 In this assignment, you will implement a program that uses a -pattern-oriented framework to spawn threads that correctly alternate -printing "Ping" and "Pong", respectively on either the command-line -display (of the Eclipse IDE) or in an Android TextView (in either the -AVD emulator or on an actual Android device). In this directory -you'll find Java source code, AndroidManifest.xml, and associated XML -metadata files. The src/edu/vuum/mocca/ directory contains the -skeleton Java code that you'll implement by completing the "TODO - You -fill in here" comments to provide a working solution. DO NOT CHANGE -THE OVERALL STRUCTURE OF THE SKELETON - just fill in the "TODO - You -fill in here" portions!!! - -In particular, you'll need to do the following: - -. Read & understand the PingPongThreadSema PingPongThreadCond classes, - which are covered in these videos: +pattern-oriented ping-pong framework to spawn threads that correctly +alternate printing "Ping" and "Pong", respectively on either the +command-line display (of the Eclipse IDE) or in an Android TextView +(in either the AVD emulator or on an actual Android device). + +In this directory you'll find Java source code, AndroidManifest.xml, +and associated XML metadata files. The directory +W5-A4-Android/src/edu/vuum/mocca/ contains the skeleton Java code that +you'll implement by completing the "TODO - You fill in here" comments +to provide a working solution. DO NOT CHANGE THE OVERALL STRUCTURE OF +THE SKELETON - just fill in the "TODO - You fill in here" portions!!! + +Although there are a lot of files, the actual amount of code you need +to write is small - the main goal is to understand the +pattern-oriented ping-pong framework and how to apply the Android +HaMeR framework. In particular, you'll need to do the following: + +. Read & understand the PingPongThreadSema and PingPongThreadCond + classes, which are covered in these videos: Section 1: Module 2: Part 10: Java Synchronization and Schedule Example @@ -29,8 +36,8 @@ In particular, you'll need to do the following: . Implement the hook methods in the AndroidPlatformStrategy.java class that use a Java CountDownLatch and Activity's runOnUiThread() method - to post a Runnable command to the UI Thread, which are covered - in these videos: + to post a Runnable command to the UI Thread, which are covered in + these videos: Section 1: Module 2: Part 9: Java CountDownLatch Section 1: Module 2: Part 10: Java Synchronization and Scheduling Example @@ -44,13 +51,7 @@ In particular, you'll need to do the following: Section 1: Module 3: Part 3: Overview of Android Handler Section 1: Module 3: Part 4: Posting and Processing Runnables to Android Handler -Note, at the moment, all these videos are available in the YouTube -playlist at - -https://www.youtube.com/playlist?list=PLZ9NgFYEMxp4tbiFYip6tDNIEBRUDyPQK - -When the 2014 POSA MOOC officially starts these videos will be -available at +These videos are available at https://class.coursera.org/posa-002/lecture @@ -61,32 +62,43 @@ in item #38 at the POSA MOOC FAQ available from http://www.courera.org/course/posa You'll need to build this Android application in the Eclipse ADT -environment. The correct solution should look like this (for both the -Eclipse command-line and Android versions): +environment. By default, the output for the correct solution should +look similar to this (for both the Eclipse command-line and Android +versions): Ready...Set...Go! -Ping!(1) -Pong!(1) -Ping!(2) -Pong!(2) -Ping!(3) -Pong!(3) -Ping!(4) -Pong!(4) -Ping!(5) -Pong!(5) -Ping!(6) -Pong!(6) -Ping!(7) -Pong!(7) -Ping!(8) -Pong!(8) -Ping!(9) -Pong!(9) -Ping!(10) -Pong!(10) +ping (1) +_pong (1) +ping (2) +_pong (2) +ping (3) +_pong (3) +ping (4) +_pong (4) +ping (5) +_pong (5) +ping (6) +_pong (6) +ping (7) +_pong (7) +ping (8) +_pong (8) +ping (9) +_pong (9) +ping (10) +_pong (10) Done! +However, this assignment only requires you to create and test the +Android version. BTW, the test assumes that each line of text is +formatted: + '\n' + +To Run the Android Application, right click on the 'W5-A4-Android' +project and select [Run As] -> [Android Application] + +To Run the Android Unit Test, right click on the 'W5-A4-Android-Test' +project and select [Run As] -> [Android Unit Test] + diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/.classpath b/assignments/week-5-assignment-4/W5-A4-Android-Test/.classpath new file mode 100644 index 000000000..2bf50ad70 --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/.project b/assignments/week-5-assignment-4/W5-A4-Android-Test/.project new file mode 100644 index 000000000..3f10b9d9c --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/.project @@ -0,0 +1,33 @@ + + + W5-A4-Android-Test + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml new file mode 100644 index 000000000..a226ce2e0 --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/assignments/week-5-assignment-4/libs/android-support-v4.jar b/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar similarity index 63% rename from assignments/week-5-assignment-4/libs/android-support-v4.jar rename to assignments/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar index 96644edbe..9056828a0 100644 Binary files a/assignments/week-5-assignment-4/libs/android-support-v4.jar and b/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar differ diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar b/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar new file mode 100644 index 000000000..1931dd97f Binary files /dev/null and b/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar differ diff --git a/assignments/week-5-assignment-4/proguard-project.txt b/assignments/week-5-assignment-4/W5-A4-Android-Test/proguard-project.txt similarity index 100% rename from assignments/week-5-assignment-4/proguard-project.txt rename to assignments/week-5-assignment-4/W5-A4-Android-Test/proguard-project.txt diff --git a/assignments/week-5-assignment-4/project.properties b/assignments/week-5-assignment-4/W5-A4-Android-Test/project.properties similarity index 96% rename from assignments/week-5-assignment-4/project.properties rename to assignments/week-5-assignment-4/W5-A4-Android-Test/project.properties index 4ab125693..a3ee5ab64 100644 --- a/assignments/week-5-assignment-4/project.properties +++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-19 +target=android-17 diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..96a442e5b Binary files /dev/null and b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png differ diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..359047dfa Binary files /dev/null and b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png differ diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..71c6d760f Binary files /dev/null and b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png differ diff --git a/assignments/week-5-assignment-4/res/values-v11/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values-v11/styles.xml similarity index 100% rename from assignments/week-5-assignment-4/res/values-v11/styles.xml rename to assignments/week-5-assignment-4/W5-A4-Android-Test/res/values-v11/styles.xml diff --git a/assignments/week-5-assignment-4/res/values-v14/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values-v14/styles.xml similarity index 100% rename from assignments/week-5-assignment-4/res/values-v14/styles.xml rename to assignments/week-5-assignment-4/W5-A4-Android-Test/res/values-v14/styles.xml diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml new file mode 100644 index 000000000..1711cecf9 --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml @@ -0,0 +1,5 @@ + + + W5-A4-Android-Test + + diff --git a/assignments/week-5-assignment-4/res/values/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/styles.xml similarity index 100% rename from assignments/week-5-assignment-4/res/values/styles.xml rename to assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/styles.xml diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java b/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java new file mode 100644 index 000000000..dcab37447 --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java @@ -0,0 +1,90 @@ +package edu.vuum.mocca.test; + +import android.content.Context; +import android.test.ActivityInstrumentationTestCase2; +import android.widget.Button; +import android.widget.TextView; +import android.view.WindowManager; + +import com.robotium.solo.Solo; + +import edu.vuum.mocca.PingPongActivity; + +/** + * @class PingPongActivityTest + * + * @brief Implements a Robotium test for the PingPongActivity. + */ +public class PingPongActivityTest + extends ActivityInstrumentationTestCase2 { + /** + * Constructor initializes the superclass. + */ + public PingPongActivityTest() { + super(PingPongActivity.class); + } + + /** + * This is the handle for Robotium, which allows us to interact + * with the UI. + */ + Solo mSolo; + + /** + * The context of this project, not the target project. + */ + Context mContext; + + Button playButton_; + TextView outputTextView_; + + /** + * Called before each test is run to perform the initialization. + */ + public void setUp() throws Exception { + super.setUp(); + + // Setup Robotium and get the EditText View. + mSolo = new Solo(getInstrumentation(), getActivity()); + + mContext = getInstrumentation().getContext(); + + playButton_ = (Button) mSolo.getView(edu.vuum.mocca.R.id.play_button); + + outputTextView_ = (TextView) mSolo + .getView(edu.vuum.mocca.R.id.pingpong_output); + + // Prevent lockscreen from preventing test. + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + }); + + getInstrumentation().callActivityOnStart(getActivity()); + getInstrumentation().callActivityOnResume(getActivity()); + } + + /** + * No need for @Test because of use of + * ActivityInstrumentationTestCase2 which makes each method in + * this class a @Test method TODO find better explanation, 100% + * proper. + */ + public void testPlayPingPongButtonPress() throws Exception { + Thread.sleep(TestOptions.ACCEPTABLE_STARTUP_LENGTH); + + // Asserttrue(outputTextView_.isAttachedToWindow() == true); + assertTrue(outputTextView_.getText().length() == 0); + + // Click on the 'play' button + mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.play_button)); + + // wait for the threads to execute + Thread.sleep(TestOptions.ACCEPTABLE_RUNTIME_LENGTH); + + assertTrue(outputTextView_.getText().toString() + .equals(TestOptions.ANDROID_TEXTVIEW)); + } +} diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java b/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java new file mode 100644 index 000000000..fb1fb4c55 --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java @@ -0,0 +1,38 @@ +package edu.vuum.mocca.test; + +/** + * @class Options + * + * @brief Holds global constants for the testing suite. More convenient than + * grabbing resources from /res and trying to finagle a working Context + * out of the test classes every time we want to use TEST_URI. + * + */ +public class TestOptions { + /** + * Time we should wait for things to run. + */ + static final long ACCEPTABLE_STARTUP_LENGTH = 1000; + + /** + * Time we should wait for things to run. + */ + static final long ACCEPTABLE_RUNTIME_LENGTH = 2000; + + /** + * Time we should wait for things to download. + */ + static final long LONG_WAIT_TIME = 5000; + + /** + * Various test strings. + */ + static final String JAVA_CONSOLE_UNIX = "Ready...Set...Go!\nping (1)\n_pong (1)\nping (2)\n_pong (2)\nping (3)\n_pong (3)\nping (4)\n_pong (4)\nping (5)\n_pong (5)\nping (6)\n_pong (6)\nping (7)\n_pong (7)\nping (8)\n_pong (8)\nping (9)\n_pong (9)\nping (10)\n_pong (10)\nDone!\n"; + static final String PING_ALL_FIRST_UNIX = "Ready...Set...Go!\nping (1)\nping (2)\nping (3)\nping (4)\nping (5)\nping (6)\nping (7)\nping (8)\nping (9)\nping (10)\n_pong (1)\n_pong (2)\n_pong (3)\n_pong (4)\n_pong (5)\n_pong (6)\n_pong (7)\n_pong (8)\n_pong (9)\n_pong (10)\nDone!\n"; + static final String PING_ALL_FIRST_WINDOWS = "Ready...Set...Go!\r\nping (1)\r\nping (2)\r\nping (3)\r\nping (4)\r\nping (5)\r\nping (6)\r\nping (7)\r\nping (8)\r\nping (9)\r\nping (10)\r\n_pong (1)\r\n_pong (2)\r\n_pong (3)\r\n_pong (4)\r\n_pong (5)\r\n_pong (6)\r\n_pong (7)\r\n_pong (8)\r\n_pong (9)\r\n_pong (10)\r\nDone!\r\n"; + static final String PONG_ALL_FIRST_WINDOWS = "Ready...Set...Go!\r\n_pong (1)\r\n_pong (2)\r\n_pong (3)\r\n_pong (4)\r\n_pong (5)\r\n_pong (6)\r\n_pong (7)\r\n_pong (8)\r\n_pong (9)\r\n_pong (10)\r\nping (1)\r\nping (2)\r\nping (3)\r\nping (4)\r\nping (5)\r\nping (6)\r\nping (7)\r\nping (8)\r\nping (9)\r\nping (10)\r\nDone!"; + static final String PONG_ALL_FIRST_UNIX = "Ready...Set...Go!\n_pong (1)\n_pong (2)\n_pong (3)\n_pong (4)\n_pong (5)\n_pong (6)\n_pong (7)\n_pong (8)\n_pong (9)\n_pong (10)\nping (1)\nping (2)\nping (3)\nping (4)\nping (5)\nping (6)\nping (7)\nping (8)\nping (9)\nping (10)\nDone!"; + static final String JAVA_CONSOLE_WINDOWS = "Ready...Set...Go!\r\nping (1)\r\n_pong (1)\r\nping (2)\r\n_pong (2)\r\nping (3)\r\n_pong (3)\r\nping (4)\r\n_pong (4)\r\nping (5)\r\n_pong (5)\r\nping (6)\r\n_pong (6)\r\nping (7)\r\n_pong (7)\r\nping (8)\r\n_pong (8)\r\nping (9)\r\n_pong (9)\r\nping (10)\r\n_pong (10)\r\nDone!"; + static final String ANDROID_TEXTVIEW = "Ready...Set...Go!\nping (1)\n_pong (1)\nping (2)\n_pong (2)\nping (3)\n_pong (3)\nping (4)\n_pong (4)\nping (5)\n_pong (5)\nping (6)\n_pong (6)\nping (7)\n_pong (7)\nping (8)\n_pong (8)\nping (9)\n_pong (9)\nping (10)\n_pong (10)\nDone!\n"; + +} diff --git a/assignments/week-5-assignment-4/W5-A4-Android/.classpath b/assignments/week-5-assignment-4/W5-A4-Android/.classpath new file mode 100644 index 000000000..51769745b --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ex/ThreadedDownload/.project b/assignments/week-5-assignment-4/W5-A4-Android/.project similarity index 96% rename from ex/ThreadedDownload/.project rename to assignments/week-5-assignment-4/W5-A4-Android/.project index 55858c982..5aad4ab5c 100644 --- a/ex/ThreadedDownload/.project +++ b/assignments/week-5-assignment-4/W5-A4-Android/.project @@ -1,6 +1,6 @@ - ThreadedDownloads + W5-A4-Android diff --git a/assignments/week-5-assignment-4/AndroidManifest.xml b/assignments/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml similarity index 86% rename from assignments/week-5-assignment-4/AndroidManifest.xml rename to assignments/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml index b7a50564e..949fedabf 100644 --- a/assignments/week-5-assignment-4/AndroidManifest.xml +++ b/assignments/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml @@ -1,12 +1,11 @@ - + android:minSdkVersion="14" + android:targetSdkVersion="19" /> + + + + + diff --git a/assignments/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml new file mode 100644 index 000000000..a91fd0372 --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/assignments/week-5-assignment-4/res/values/dimens.xml b/assignments/week-5-assignment-4/W5-A4-Android/res/values/dimens.xml similarity index 100% rename from assignments/week-5-assignment-4/res/values/dimens.xml rename to assignments/week-5-assignment-4/W5-A4-Android/res/values/dimens.xml diff --git a/assignments/week-5-assignment-4/res/values/strings.xml b/assignments/week-5-assignment-4/W5-A4-Android/res/values/strings.xml similarity index 71% rename from assignments/week-5-assignment-4/res/values/strings.xml rename to assignments/week-5-assignment-4/W5-A4-Android/res/values/strings.xml index 0beb6a933..cd5196b9a 100644 --- a/assignments/week-5-assignment-4/res/values/strings.xml +++ b/assignments/week-5-assignment-4/W5-A4-Android/res/values/strings.xml @@ -1,8 +1,9 @@ - - PingPongFinal + + PlayPingPong Settings Play PingPong! Reset Game + diff --git a/assignments/week-5-assignment-4/W5-A4-Android/res/values/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android/res/values/styles.xml new file mode 100644 index 000000000..6ce89c7ba --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/AndroidPlatformStrategy.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java similarity index 91% rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/AndroidPlatformStrategy.java rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java index 83dc2a05d..bc684b5ce 100644 --- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/AndroidPlatformStrategy.java +++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java @@ -45,7 +45,7 @@ public AndroidPlatformStrategy(Object output, /** Do any initialization needed to start a new game. */ public void begin() { - /** Reset the CountDownLatch. */ + /** (Re)initialize the CountDownLatch. */ // TODO - You fill in here. } @@ -71,12 +71,6 @@ public void awaitDone() // TODO - You fill in here. } - /** Returns the platform name in a String. */ - public String platformName() - { - return System.getProperty("java.specification.vendor"); - } - /** * Error log formats the message and displays it for the * debugging purposes. @@ -86,4 +80,3 @@ public void errorLog(String javaFile, String errorMessage) Log.e(javaFile, errorMessage); } } - diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/ConsolePlatformStrategy.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java similarity index 78% rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/ConsolePlatformStrategy.java rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java index c67f981ca..c83233f16 100644 --- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/ConsolePlatformStrategy.java +++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java @@ -17,13 +17,13 @@ public class ConsolePlatformStrategy extends PlatformStrategy * Latch to decrement each time a thread exits to control when the * play() method returns. */ - private static CountDownLatch mLatch = new CountDownLatch(2); + private static CountDownLatch mLatch = null; /** Contains information for outputting to console window. */ PrintStream mOutput; /** Ctor. */ - ConsolePlatformStrategy(Object output) + public ConsolePlatformStrategy(Object output) { mOutput = (PrintStream) output; } @@ -31,14 +31,14 @@ public class ConsolePlatformStrategy extends PlatformStrategy /** Do any initialization needed to start a new game. */ public void begin() { - mLatch = new CountDownLatch(2); + mLatch = new CountDownLatch(NUMBER_OF_THREADS); } /** Print the outputString to the display. */ public void print(String outputString) { /** Print to the console window. */ - System.out.println(outputString); + mOutput.println(outputString); } /** Indicate that a game thread has finished running. */ @@ -56,18 +56,13 @@ public void awaitDone() } } - /** Returns a string revealing the platform in use. */ - public String platformName() - { - return System.getProperty("java.specification.vendor"); - } - /** * Error log formats the message and displays it for the debugging * purposes. */ public void errorLog(String javaFile, String errorMessage) { - System.out.println(javaFile + " " + errorMessage); + mOutput.println(javaFile + " " + errorMessage); } } + diff --git a/ex/PingPong/console/src/edu/vuum/mocca/Main.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Main.java similarity index 96% rename from ex/PingPong/console/src/edu/vuum/mocca/Main.java rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Main.java index 9026c80e6..03533c545 100644 --- a/ex/PingPong/console/src/edu/vuum/mocca/Main.java +++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Main.java @@ -12,7 +12,7 @@ public class Main * The Java virtual machine requires the instantiation of a main * method to run the console version of the PlayPingPong app. */ - public static void main(String[] args) + public static void main(String[] args) { /** * Initializes the Platform singleton with the appropriate diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/Options.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java similarity index 79% rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/Options.java rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java index 11d85015f..f07a88350 100644 --- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/Options.java +++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java @@ -1,9 +1,5 @@ package edu.vuum.mocca; -import java.io.File; -import java.nio.*; -import java.util.*; - /** * @class Options * @@ -62,18 +58,20 @@ public String syncMechanism() */ public boolean parseArgs(String argv[]) { - for (int argc = 0; argc < argv.length; argc += 2) - if (argv[argc].equals("-i")) - mMaxIterations = Integer.parseInt(argv[argc + 1]); - else if (argv[argc].equals("-s")) - mSyncMechanism = argv[argc + 1]; - else if (argv[argc].equals("-t")) - mMaxTurns = Integer.parseInt(argv[argc + 1]); - else - { - printUsage(); - return false; - } + if (argv != null) { + for (int argc = 0; argc < argv.length; argc += 2) + if (argv[argc].equals("-i")) + mMaxIterations = Integer.parseInt(argv[argc + 1]); + else if (argv[argc].equals("-s")) + mSyncMechanism = argv[argc + 1]; + else if (argv[argc].equals("-t")) + mMaxTurns = Integer.parseInt(argv[argc + 1]); + else + { + printUsage(); + return false; + } + } return true; } diff --git a/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java new file mode 100644 index 000000000..3d283d399 --- /dev/null +++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java @@ -0,0 +1,76 @@ +package edu.vuum.mocca; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +/** + * @class MainActivity + * + * @brief Initial start up screen for the android GUI. + */ +public class PingPongActivity extends Activity { + /** TextView that PingPong will be "played" upon */ + private TextView mAndroidPingPongOutput; + + /** Button that allows playing and resetting of the game */ + private Button mPlayButton; + + /** Variables to track state of the game */ + private static int PLAY = 0; + private static int RESET = 1; + private int mGameState = PLAY; + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Sets the content view to the xml file, activity_ping_pong. + setContentView(R.layout.activity_ping_pong); + mAndroidPingPongOutput = + (TextView) findViewById(R.id.pingpong_output); + mPlayButton = (Button) findViewById(R.id.play_button); + + // Initializes the Platform singleton with the appropriate + // Platform strategy, which in this case will be the + // AndroidPlatform. + PlatformStrategy.instance + (new PlatformStrategyFactory + (mAndroidPingPongOutput, + this).makePlatformStrategy()); + + // Initializes the Options singleton. + Options.instance().parseArgs(null); + } + + /** Sets the action of the button on click state. */ + public void playButtonClicked(View view) { + if (mGameState == PLAY) { + // Use a factory method to create the appropriate type of + // OutputStrategy. + PlayPingPong pingPong = + new PlayPingPong(PlatformStrategy.instance(), + Options.instance().maxIterations(), + Options.instance().maxTurns(), + Options.instance().syncMechanism()); + + // Play ping-pong with the designated number of + // iterations. + new Thread(pingPong).start(); + mPlayButton.setText(R.string.reset_button); + mGameState = RESET; + } else if (mGameState == RESET) { + + // Empty TextView and prepare the UI to play another game. + mAndroidPingPongOutput.setText(R.string.empty_string); + mPlayButton.setText(R.string.play_button); + mGameState = PLAY; + } else { + // Notify the player that something has gone wrong and + // reset. + mAndroidPingPongOutput.setText("Unknown State entered!"); + mGameState = RESET; + } + } +} diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategy.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java similarity index 87% rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategy.java rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java index 1280920f2..8ae0fadef 100644 --- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategy.java +++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java @@ -12,6 +12,9 @@ */ public abstract class PlatformStrategy { + /** Number of threads used to play ping-pong. */ + protected static final int NUMBER_OF_THREADS = 2; + /** The singleton @a PlatformStrategy instance. */ private static PlatformStrategy mUniqueInstance = null; @@ -42,11 +45,13 @@ public static PlatformStrategy instance(PlatformStrategy platform) /** Barrier that waits for all the game threads to finish. */ public abstract void awaitDone(); - /** + /** * Returns the name of the platform in a string. e.g., Android or * a JVM. + * @deprecated This method is just here for backwards + * compatibility with the skeletons. */ - public abstract String platformName(); + public String platformName() { return ""; } /** * Error log formats the message and displays it for the debugging diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategyFactory.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java similarity index 68% rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategyFactory.java rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java index a655f6a18..95d6bb962 100644 --- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategyFactory.java +++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java @@ -11,21 +11,29 @@ public class PlatformStrategyFactory { /** - * This interface uses the Strategy pattern to create @a PlatformStrategy - * implementations at runtime. + * This interface uses the Strategy pattern to create @a + * PlatformStrategy implementations at runtime. */ private static interface IPlatformStrategyFactoryStrategy { public PlatformStrategy execute(); } + /** + * Enumeration distinguishing platforms Android from plain ol' Java. + */ + public enum PlatformType { + ANDROID, + PLAIN_JAVA + } + /** * HashMap used to map strings containing the Java platform names * and dispatch the execute() method of the associated @a PlatformStrategy * implementation. */ - private HashMap mPlatformStrategyMap = - new HashMap(); + private HashMap mPlatformStrategyMap = + new HashMap(); /** * Ctor that stores the objects that perform output for a @@ -39,7 +47,7 @@ public PlatformStrategyFactory(final Object output, * The "The Android Project" string maps to a command object * that creates an @a AndroidPlatformStrategy implementation. */ - mPlatformStrategyMap.put("The Android Project", + mPlatformStrategyMap.put(PlatformType.ANDROID, new IPlatformStrategyFactoryStrategy() { /** @@ -58,7 +66,7 @@ public PlatformStrategy execute() * The "Sun Microsystems Inc." string maps to a command object * that creates an @a ConsolePlatformStrategy implementation. */ - mPlatformStrategyMap.put("Sun Microsystems Inc.", + mPlatformStrategyMap.put(PlatformType.PLAIN_JAVA, new IPlatformStrategyFactoryStrategy() { public PlatformStrategy execute() @@ -66,19 +74,26 @@ public PlatformStrategy execute() return new ConsolePlatformStrategy(output); } }); + } - /** - * The "Oracle Corporation" string maps to a command object - * that creates an @a ConsolePlatformStrategy implementation. - */ - mPlatformStrategyMap.put("Oracle Corporation", - new IPlatformStrategyFactoryStrategy() - { - public PlatformStrategy execute() - { - return new ConsolePlatformStrategy(output); - } - }); + /** + * Returns the name of the platform in a string. e.g., Android or + * a JVM. + */ + public static String platformName() + { + return System.getProperty("java.specification.vendor"); + } + + /** + * Returns the type of the platformm e.g. Android or + * a JVM. + */ + public static PlatformType platformType() { + if(platformName().indexOf("Android") >= 0) + return PlatformType.ANDROID; + else + return PlatformType.PLAIN_JAVA; } /** @@ -87,8 +102,8 @@ public PlatformStrategy execute() */ public PlatformStrategy makePlatformStrategy() { - String name = System.getProperty("java.specification.vendor"); + PlatformType type = platformType(); - return mPlatformStrategyMap.get(name).execute(); + return mPlatformStrategyMap.get(type).execute(); } } diff --git a/ex/PingPong/console/src/edu/vuum/mocca/PlayPingPong.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java similarity index 62% rename from ex/PingPong/console/src/edu/vuum/mocca/PlayPingPong.java rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java index 2e18703c7..e3111342a 100644 --- a/ex/PingPong/console/src/edu/vuum/mocca/PlayPingPong.java +++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java @@ -3,66 +3,61 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.CountDownLatch; /** * @class PlayPingPong - * - * @brief This class implements a Java program that creates two - * threads, Ping and Pong, to alternately print "Ping" and - * "Pong", respectively, on the display. It uses the Template - * Method, Strategy, and Factory Method patterns to factor out - * common code and simplify the program design. + * + * @brief This class implements a Java program that creates two threads, Ping + * and Pong, to alternately print "Ping" and "Pong", respectively, on the + * display. It uses the Template Method, Strategy, and Factory Method + * patterns to factor out common code and simplify the program design. */ -public class PlayPingPong implements Runnable -{ +public class PlayPingPong implements Runnable { /** * Number of iterations to ping/pong. */ private static volatile int mMaxIterations; /** Maximum number of iterations per "turn" (defaults to 1). */ - private static volatile int mMaxTurns = 1; + private static int mMaxTurns = 1; /** - * Keeps track of the platform that we're running on, e.g., - * Android vs. Console. + * Keeps track of the platform that we're running on, e.g., Android vs. + * Console. */ private static volatile PlatformStrategy mPlatformStrategy; /** - * Which synchronization to use, e.g., "SEMA" vs. "COND". - * Defaults to "SEMA". + * Which synchronization to use, e.g., "SEMA" vs. "COND". Defaults to + * "SEMA". */ private static String mSyncMechanism = "SEMA"; - + /** - * Constants used to distinguish between ping and pong threads. + * Constants used to distinguish between ping and pong threads. */ private final static int PING_THREAD = 0; private final static int PONG_THREAD = 1; /** * @Brief PingPongThread - * + * * @class This class implements the core ping/pong algorithm, but - * defers the scheduling aspect to subclasses. It plays - * the role of the "Abstract Class" in the Template Method + * defers the scheduling aspect to subclasses. It plays the + * role of the "Abstract Class" in the Template Method * pattern. */ - static abstract class PingPongThread extends Thread - { - /** + static abstract class PingPongThread extends Thread { + /** * Constructor initializes the various fields. */ - PingPongThread(String stringToPrint) - { + PingPongThread(String stringToPrint) { mStringToPrint = stringToPrint; } /** - * Abstract hook methods that determine the ping/pong - * scheduling protocol in the run() template method. + * Abstract hook methods that determine the ping/pong scheduling + * protocol in the run() template method. */ abstract void acquire(); abstract void release(); @@ -70,28 +65,25 @@ static abstract class PingPongThread extends Thread /** * Sets the id of the other thread. */ - void setOtherThreadId(long id) {} - + void setOtherThreadId(long id) { + } + /** * This method runs in a separate thread of control and - * implements the core ping/pong algorithm. It plays the role + * implements the core ping/pong algorithm. It plays the role * of the "template method" in the Template Method pattern. */ - public void run() - { - for (int loopsDone = 1; - loopsDone <= mMaxIterations; - ++loopsDone) { + public void run() { + for (int loopsDone = 1; loopsDone <= mMaxIterations; ++loopsDone) { // Perform the template method protocol for printing a - // "ping" or a "pong" on the display. Note that the + // "ping" or a "pong" on the display. Note that the // acquire() and release() hook methods that control // the scheduling of the threads are deferred to // subclasses. acquire(); - mPlatformStrategy.print - (mStringToPrint + "(" + loopsDone + ")"); + mPlatformStrategy.print(mStringToPrint + "(" + loopsDone + ")"); release(); } @@ -104,19 +96,18 @@ public void run() // Data member that indicates the string to print (typically a // "ping" or a "pong"). - protected String mStringToPrint; + protected final String mStringToPrint; } /** * @class PingPongThreadSema - * - * @brief This class uses semaphores to implement the acquire() - * and release() hook methods that schedule the ping/pong - * algorithm. It plays the role of the "Concrete Class" in - * the Template Method pattern. + * + * @brief This class uses semaphores to implement the acquire() and + * release() hook methods that schedule the ping/pong algorithm. It + * plays the role of the "Concrete Class" in the Template Method + * pattern. */ - static class PingPongThreadSema extends PingPongThread - { + static class PingPongThreadSema extends PingPongThread { /** * Semaphores that schedule the ping/pong algorithm */ @@ -128,10 +119,8 @@ static class PingPongThreadSema extends PingPongThread private final static int FIRST_SEMA = 0; private final static int SECOND_SEMA = 1; - PingPongThreadSema(String stringToPrint, - Semaphore firstSema, - Semaphore secondSema) - { + PingPongThreadSema(String stringToPrint, Semaphore firstSema, + Semaphore secondSema) { super(stringToPrint); mSemas[FIRST_SEMA] = firstSema; mSemas[SECOND_SEMA] = secondSema; @@ -140,16 +129,14 @@ static class PingPongThreadSema extends PingPongThread /** * Hook method for ping/pong acquire. */ - void acquire() - { + void acquire() { mSemas[FIRST_SEMA].acquireUninterruptibly(); } /** * Hook method for ping/pong release. */ - void release() - { + void release() { mSemas[SECOND_SEMA].release(); } @@ -163,8 +150,7 @@ void release() * plays the role of the "Concrete Class" in the Template Method * pattern. */ - static class PingPongThreadCond extends PingPongThread - { + static class PingPongThreadCond extends PingPongThread { /** * Semaphores that schedule the ping/pong algorithm. */ @@ -179,7 +165,7 @@ static class PingPongThreadCond extends PingPongThread * Number of times we've iterated thus far in our "turn". */ private int mIterationCount = 0; - + /** * Id for the other thread. */ @@ -189,9 +175,8 @@ static class PingPongThreadCond extends PingPongThread * Thread whose turn it currently is. */ private static long mThreadOwner; - - public void setOtherThreadId(long otherThreadId) - { + + public void setOtherThreadId(long otherThreadId) { this.mOtherThreadId = otherThreadId; } @@ -201,18 +186,14 @@ public void setOtherThreadId(long otherThreadId) private final static int FIRST_COND = 0; private final static int SECOND_COND = 1; - PingPongThreadCond(String stringToPrint, - ReentrantLock lock, - Condition firstCond, - Condition secondCond, - boolean isOwner) - { + PingPongThreadCond(String stringToPrint, ReentrantLock lock, + Condition firstCond, Condition secondCond, boolean isOwner) { super(stringToPrint); mIterationCount = mMaxTurns; mLock = lock; mConds[FIRST_COND] = firstCond; mConds[SECOND_COND] = secondCond; - if (isOwner) + if (isOwner) mThreadOwner = this.getId(); } @@ -247,14 +228,11 @@ void release() { } /** - * Constructor stores the PlatformStrategy and the number of - * iterations to play ping/pong. + * Constructor stores the PlatformStrategy and the number of iterations to + * play ping/pong. */ - public PlayPingPong (PlatformStrategy platformStrategy, - int maxIterations, - int maxTurns, - String syncMechanism) - { + public PlayPingPong(PlatformStrategy platformStrategy, int maxIterations, + int maxTurns, String syncMechanism) { // The PlatformStrategy being used. mPlatformStrategy = platformStrategy; @@ -263,57 +241,62 @@ public PlayPingPong (PlatformStrategy platformStrategy, // Number of iterations to perform pings and pongs per "turn". mMaxTurns = maxTurns; - + // Which synchronization to use (e.g., "SEMA" vs. "COND"). mSyncMechanism = syncMechanism; } - private void makePingPongThreads(String schedMechanism, - PingPongThread[] pingPongThreads) - { + static String pingString = "ping "; + static String pongString = "_pong "; + + static boolean checkedStringFormatting = false; + + private void formatStrings() { + if (!checkedStringFormatting) { + PlatformStrategyFactory.PlatformType type = + PlatformStrategyFactory.PlatformType.ANDROID; + + if (PlatformStrategyFactory.platformType().equals(type)) + pingString += " "; + checkedStringFormatting = true; + } + } + + private void makePingPongThreads(String schedMechanism, + PingPongThread[] pingPongThreads) { + formatStrings(); if (schedMechanism.equals("SEMA")) { // Create the semaphores that schedule threads - // printing "ping" and "pong" in the correct + // printing "ping " and "_pong" in the correct // alternating order. Semaphore pingSema = new Semaphore(1); // Starts out unlocked. Semaphore pongSema = new Semaphore(0); - - pingPongThreads[PING_THREAD] = - new PingPongThreadSema("ping", pingSema, pongSema); - pingPongThreads[PONG_THREAD] = - new PingPongThreadSema("pong", pongSema, pingSema); - } - else if (schedMechanism.equals("COND")) { + + pingPongThreads[PING_THREAD] = new PingPongThreadSema(pingString, + pingSema, pongSema); + pingPongThreads[PONG_THREAD] = new PingPongThreadSema(pongString, + pongSema, pingSema); + } else if (schedMechanism.equals("COND")) { ReentrantLock lock = new ReentrantLock(); Condition pingCond = lock.newCondition(); Condition pongCond = lock.newCondition(); - int numberOfTurnsEach = 2; - - pingPongThreads[PING_THREAD] = - new PingPongThreadCond("ping", - lock, - pingCond, - pongCond, - true); - pingPongThreads[PONG_THREAD] = - new PingPongThreadCond("pong", - lock, - pongCond, - pingCond, - false); + + pingPongThreads[PING_THREAD] = new PingPongThreadCond(pingString, + lock, pingCond, pongCond, true); + pingPongThreads[PONG_THREAD] = new PingPongThreadCond(pongString, + lock, pongCond, pingCond, false); pingPongThreads[PING_THREAD] .setOtherThreadId(pingPongThreads[PONG_THREAD].getId()); pingPongThreads[PONG_THREAD] .setOtherThreadId(pingPongThreads[PING_THREAD].getId()); } } - + /** - * Start running the ping/pong code, which can be called from a - * main() function in a Java class, an Android Activity, etc. - */ - public void run() - { + * Start running the ping/pong code, which can be called from a main() + * function in a Java class, an Android Activity, etc. + */ + public void run() { // Indicate a new game is beginning. mPlatformStrategy.begin(); @@ -325,24 +308,22 @@ public void run() pingPongThreads[PING_THREAD] = null; pingPongThreads[PONG_THREAD] = null; - /** - * Create the appropriate type of threads with the designated - * scheduling mechanism (e.g., "SEMA" for Semaphores, "COND" - * for ConditionObjects, etc.). + /** + * Create the appropriate type of threads with the designated scheduling + * mechanism (e.g., "SEMA" for Semaphores, "COND" for ConditionObjects, + * etc.). */ - makePingPongThreads(mSyncMechanism, - pingPongThreads); + makePingPongThreads(mSyncMechanism, pingPongThreads); /** - * Start ping and pong threads, which calls their run() - * methods. + * Start ping and pong threads, which calls their run() methods. */ pingPongThreads[PING_THREAD].start(); pingPongThreads[PONG_THREAD].start(); /** - * Barrier synchronization to wait for all work to be done - * before exiting play(). + * Barrier synchronization to wait for all work to be done before + * exiting play(). */ mPlatformStrategy.awaitDone(); diff --git a/assignments/week-5-assignment-4/ic_launcher-web.png b/assignments/week-5-assignment-4/ic_launcher-web.png deleted file mode 100644 index a18cbb48c..000000000 Binary files a/assignments/week-5-assignment-4/ic_launcher-web.png and /dev/null differ diff --git a/assignments/week-5-assignment-4/res/drawable-hdpi/ic_launcher.png b/assignments/week-5-assignment-4/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 288b66551..000000000 Binary files a/assignments/week-5-assignment-4/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/assignments/week-5-assignment-4/res/drawable-mdpi/ic_launcher.png b/assignments/week-5-assignment-4/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 6ae570b4d..000000000 Binary files a/assignments/week-5-assignment-4/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/assignments/week-5-assignment-4/res/drawable-xhdpi/ic_launcher.png b/assignments/week-5-assignment-4/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index d4fb7cd9d..000000000 Binary files a/assignments/week-5-assignment-4/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/assignments/week-5-assignment-4/res/drawable-xxhdpi/ic_launcher.png b/assignments/week-5-assignment-4/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index 85a608158..000000000 Binary files a/assignments/week-5-assignment-4/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PingPongActivity.java b/assignments/week-5-assignment-4/src/edu/vuum/mocca/PingPongActivity.java deleted file mode 100644 index 4296cd074..000000000 --- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PingPongActivity.java +++ /dev/null @@ -1,85 +0,0 @@ -package edu.vuum.mocca; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; - -/** - * @class MainActivity - * - * @brief Initial start up screen for the android GUI. - */ -public class PingPongActivity extends Activity -{ - /** TextView that PingPong will be "played" upon */ - private TextView mAndroidPingPongOutput; - - /** Button that allows playing and resetting of the game */ - private Button mPlayButton; - - /** Variables to track state of the game */ - private static int PLAY = 0; - private static int RESET = 1; - private int mGameState = PLAY; - - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - /** Sets the content view to the xml file, activity_ping_pong. */ - setContentView(R.layout.activity_ping_pong); - mAndroidPingPongOutput = (TextView) findViewById(R.id.pingpong_output); - mPlayButton = (Button) findViewById(R.id.play_button); - - /** - * Initializes the Platform singleton with the appropriate - * Platform strategy, which in this case will be the - * AndroidPlatform. - */ - PlatformStrategy.instance - (new PlatformStrategyFactory - (mAndroidPingPongOutput, this).makePlatformStrategy()); - - /** Initializes the Options singleton. */ - String args[] = new String[] { "PlayPingPongGUI" }; - Options.instance().parseArgs(args); - } - - /** Sets the action of the button on click state. */ - public void playButtonClicked(View view) - { - if(mGameState == PLAY) { - /** - * Use a factory method to create the appropriate type of - * OutputStrategy. - */ - PlayPingPong pingPong = - new PlayPingPong(PlatformStrategy.instance(), - Options.instance().maxIterations(), - Options.instance().maxTurns(), - Options.instance().syncMechanism()); - - /** - * Play ping-pong with the designated number of - * iterations. - */ - new Thread (pingPong).start(); - mPlayButton.setText(R.string.reset_button); - mGameState = RESET; - } - else if(mGameState == RESET) { - - /** Empty TextView and prepare the UI to play another game */ - mAndroidPingPongOutput.setText(R.string.empty_string); - mPlayButton.setText(R.string.play_button); - mGameState = PLAY; - } - else { - /** Notify the player that something has gone wrong and reset */ - mAndroidPingPongOutput.setText("Unknown State entered!"); - mGameState = RESET; - } - } -} diff --git a/assignments/week-6-assignment-5/Assignment-Description.txt b/assignments/week-6-assignment-5/Assignment-Description.txt new file mode 100644 index 000000000..b1752ceaf --- /dev/null +++ b/assignments/week-6-assignment-5/Assignment-Description.txt @@ -0,0 +1,114 @@ +Week 6: Programming Assignment 5 + +Released Monday, June 16th, 2014 +Due Monday, June 30th, 2014 + +In this assignment, you will implement a program that downloads images +using started services and then displays them in the context of the UI +Thread. The user can optionally select one of two different Started +Services whose implementations you will complete: + +. A simple DownloadIntentService that uses on the Android + IntentService framework to download files in a single background + Thread and + +. A more scalable ThreadPoolDownloadService that uses a + ThreadPoolExecutor to download several files concurrently within a + fixed pool of Threads. + +In this directory you'll find Java source code, AndroidManifest.xml, +and associated XML metadata files. The directory +W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ contains +the skeleton Java code that you'll implement by completing the "TODO - +You fill in here" comments to provide a working solution. DO NOT +CHANGE THE OVERALL STRUCTURE OF THE SKELETON - just fill in the "TODO +- You fill in here" portions!!! + +The main goal of this project is to understand the pattern-oriented +threaded download application and how to start and interact with +Services using the IntentService and Messenger IPC mechanisms. In +particular, you'll need to do the following: + +. Understand how to launch Started Services using Intents and receive + replies from Services using the Messenger IPC mechanism in the + DownloadActivity class, which are covered in these videos: + + Section 1: Module 3: Part 5: Sending and Handling Messages with Android Handler + Section 2: Module 1: Part 2: Programming Started Services (Part 1) + Section 2: Module 1: Part 3: Programming Started Services (Part 2) + +. Understand how to implement the DownloadIntentService and + ThreadPoolDownloadService by handling Intents, doing work in the + background, and sending results the Messenger IPC mechanism covered + in these videos: + + Section 2: Module 1: Part 4: Android IntentService + Section 2: Module 1: Part 6: Service to Activity Communication via Android Messenger + +. Understand factory methods and implement the makeIntent() factory + methods in each service, which are similar to the + DownloadService.makeIntent() factory method covered in this video: + + Section 2: Module 1: Part 3: Programming Started Services (Part 2) + +. Understand the ThreadPoolExecutor class and utilize a factory method + in the Executors class to create a new thread pool, which is + (briefly) outlined in this video + + Section 1: Module 3: Part 7: The AsyncTask Framework (Part 2) + + and described further in the documentation at: + + http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html + + There's also an example that shows how to use a Executor thread pool + available at + + https://github.com/douglascraigschmidt/POSA-14/tree/master/ex/UniqueIDGeneratorApplication + +. Understand the methods provided in the DownloadUtils class, which + contains functionality that is common to both the + DownloadIntentService and ThreadPoolDownloadService. + +These videos are available at + +https://class.coursera.org/posa-002/lecture + +We'll also discuss this assignment specification (and later its +solution) in the POSA MOOC "Virtual Office Hours", which are described +in item #38 at the POSA MOOC FAQ available from + +http://www.courera.org/course/posa + +You'll need to build this Android application in the Eclipse ADT +environment. Unlike previous assignments, this program is +user-driven, so output will vary depending on which URL is provided. +In addition to using the default URL (which is available from +https://d396qusza40orc.cloudfront.net/posa/ka.png) you should also +test this application by finding an Internet URL to an image (ending +in .jpg, .gif, .png etc.) and see if your application correctly +displays the image. + +We provide several JUnit tests that should test your program's +functionality. The unit tests test the Service classes independently +of the DownloadActivity, and then test the overall functionality of +the program. By default, the unit tests try to download the default +URL and then check if the proper image was downloaded using Bitmap +comparison. + +To Run the Android Application, right click on the 'W6-A5-Android' +project and select [Run As] -> [Android Application] + +To Run the Android Unit Test, right click on the 'W6-A5-Android-Test' +project and select [Run As] -> [Android Unit Test] + +By default, the application and test program do an "offline" download +since students often run this code an emulator or some other +environment with slow/no Internet connectivity. If you'd like to +run/test it in the Internet please change the DOWNLOAD_OFFLINE boolean +in + +W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java + +to false. + diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath new file mode 100644 index 000000000..de7aa1845 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project new file mode 100644 index 000000000..04245676b --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project @@ -0,0 +1,34 @@ + + + W6-A5-ThreadedDownloads-StartedServices-Tests + + + week-6-assignment-5 + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml new file mode 100644 index 000000000..2d80b1796 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties new file mode 100644 index 000000000..a3ee5ab64 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..96a442e5b Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 000000000..99238729d Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..359047dfa Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg new file mode 100644 index 000000000..e31e8b98b Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..71c6d760f Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml new file mode 100644 index 000000000..111dfea6e --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml @@ -0,0 +1,5 @@ + + + + W6-A5-ThreadedDownloads-StartedServices-Tests + diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java new file mode 100644 index 000000000..0d878f947 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java @@ -0,0 +1,22 @@ +package edu.vuum.mocca.test; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * @class AllTests + * + * @brief Combine all the unit tests into one suite, so that you can run them together with one click. + */ +public class AllTests { + public static Test suite() { + TestSuite suite = new TestSuite(AllTests.class.getName()); + //$JUnit-BEGIN$ + suite.addTestSuite(DownloadIntentServiceTests.class); + suite.addTestSuite(DownloadUtilsTests.class); + suite.addTestSuite(ThreadPoolDownloadServiceTests.class); + suite.addTestSuite(DownloadActivityTests.class); + //$JUnit-END$ + return suite; + } +} diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java new file mode 100644 index 000000000..991dd170e --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java @@ -0,0 +1,121 @@ +package edu.vuum.mocca.test; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.test.ActivityInstrumentationTestCase2; +import android.widget.EditText; +import android.view.WindowManager; + +import com.robotium.solo.Solo; + +import edu.vuum.mocca.DownloadActivity; + +/** + * @class DownloadActivityTests + * + * @brief Tests the functionality of DownloadActivity. Current tests + * only press buttons and check that the correct image was downloaded. + */ +public class DownloadActivityTests + extends + ActivityInstrumentationTestCase2 { + /** + * Constructor initializes the superclass + */ + public DownloadActivityTests() { + super(DownloadActivity.class); + } + + /** + * This is the handle for Robotium, which allows us to interact + * with the UI. + */ + Solo mSolo; + + /** + * The context of this project, not the target project. + */ + Context mContext; + + /** + * The activity we're testing + */ + DownloadActivity mActivity; + + /** + * Store the bitmap that we expect to be downloaded. + */ + Bitmap mExpected; + + /** + * Called before each test is run to perform the initialization. + */ + public void setUp() throws Exception{ + super.setUp(); + + mActivity = getActivity(); + + // Setup Robotium and get the EditText View. + mSolo = new Solo(getInstrumentation(), mActivity); + + mContext = getInstrumentation().getContext(); + + EditText edit_ = (EditText) mSolo.getView(edu.vuum.mocca.R.id.url); + mSolo.clearEditText(edit_); + mSolo.enterText(edit_, Options.TEST_URI); + + mExpected = BitmapFactory.decodeResource(mContext.getResources(), + R.drawable.dougs); + + getInstrumentation().callActivityOnStart(mActivity); + getInstrumentation().callActivityOnResume(mActivity); + + // Let us dismiss the lockscreen + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + }); + + // Wait for things to settle + Thread.sleep(Options.SHORT_WAIT_TIME); + } + + /** + * Push the button and see if the correct image is displayed. + */ + public void testThreadPoolButton() throws Exception { + + // Make sure the current image isn't the one we're looking for + assertFalse(mExpected.sameAs(mActivity.mCurrentBitmap)); + + // Click on the "Start ThreadPool Button" + mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.thread_pool_button)); + + // Wait for the image to download + Thread.sleep(Options.LONG_WAIT_TIME); + + // Check if we displayed the correct image + assertTrue(mExpected.sameAs(mActivity.mCurrentBitmap)); + } + + /** + * Push the button and see if the correct image is displayed. + */ + public void testIntentServiceButton() throws Exception { + + // Make sure the current image isn't the one we're looking for + assertFalse(mExpected.sameAs(mActivity.mCurrentBitmap)); + + // Click on the "Start ThreadPool Button" + mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.intent_service_button)); + + // Wait for the image to download + Thread.sleep(Options.LONG_WAIT_TIME); + + // Check if we displayed the correct image + assertTrue(mExpected.sameAs(mActivity.mCurrentBitmap)); + } +} diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java new file mode 100644 index 000000000..8148d4dac --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java @@ -0,0 +1,162 @@ +package edu.vuum.mocca.test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.test.ServiceTestCase; +import edu.vuum.mocca.DownloadIntentService; +import edu.vuum.mocca.DownloadUtils; + +/** + * @class DownloadIntentServiceTests + * + * @brief Tests the functionality of the DownloadIntentService. + */ +public class DownloadIntentServiceTests + extends + ServiceTestCase { + /** + * Use this latch to wait for messages to be received by the + * Handler's thread. + */ + static CountDownLatch mLatch; + + /** + * Stores the Uri received by the Handler. + */ + static String mReceivedUri = null; + + /** + * The Handler that receives a Message reply from the + * IntentService. + */ + static MessageHandler mHandler; + + /** + * Store the intent from makeIntent() to test proper creations. + */ + Intent mIntent; + + /** + * The context of THIS project, not the target project + */ + Context mContext; + + /** + * Constructor initializes the superclass. + */ + public DownloadIntentServiceTests() { + super(DownloadIntentService.class); + } + + /** + * @class MessageHandler + * + * @brief Make a handler to catch Messages sent from the service. + */ + static class MessageHandler extends Handler { + /** + * Allows us to explicitly specify which thread's Looper to use. + */ + public MessageHandler(Looper myLooper) { + super(myLooper); + } + + /** + * Handle return messages from the IntentService. Store the + * received Uri and notify the JUnit thread. + */ + public void handleMessage(Message msg) { + // We don't know what tag they'll use for the path, so we + // have to search for it. + mReceivedUri = Utilities.searchForPath(msg); + + // Let the unit test thread know we've gotten a message. + mLatch.countDown(); + } + } + + /** + * This method is called before each test is called to perform + * initialization activities. + */ + public void setUp() throws Exception{ + super.setUp(); + + // Make an intent with the handler using the factory method. + mIntent = DownloadIntentService.makeIntent(getContext(), + new Handler(), + Options.TEST_URI); + + // Get the context for THIS project, not the target project. + mContext = getContext().createPackageContext(this.getClass().getPackage().getName(), + Context.CONTEXT_IGNORE_SECURITY); + + } + + /** + * Check that the intent starts the correct service. + */ + public void test_makeIntent_Class () { + assertTrue(Utilities.checkClass(mIntent, DownloadIntentService.class)); + } + + /** + * Check that the intent has the correct Uri. + */ + public void test_makeIntent_Uri () { + assertTrue(Utilities.checkUri(mIntent)); + } + + /** + * Check that the intent has a Messenger attached. + */ + public void test_makeIntent_Messenger () { + assertTrue(Utilities.checkMessenger(mIntent)); + } + + /** + * Try starting the service + */ + public void test_startService () throws Exception { + + mLatch = new CountDownLatch(1); + + // Start a thread to handle the message when it's sent. + new Thread(new Runnable() { + public void run() { + Looper.prepare(); + mHandler = new MessageHandler(Looper.myLooper()); + Looper.loop(); + } + }).start(); + + // Wait for the handler to get instantiated + Thread.sleep(Options.SHORT_WAIT_TIME); + + //Create an Intent to start the service + mIntent = DownloadIntentService.makeIntent(getContext(), + mHandler, + Options.TEST_URI); + + // Start the service + startService(mIntent); + + assertNotNull(getService()); + + // Wait for it to send us a Message (or time out) + mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS); + + // Check if we got a Message or timed out + assertNotNull(mReceivedUri); + + // Check that the image actually downloaded. + assertTrue(Utilities.checkDownloadedImage(mContext, mReceivedUri)); + } +} diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java new file mode 100644 index 000000000..d82d0087b --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java @@ -0,0 +1,202 @@ +package edu.vuum.mocca.test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.test.ActivityInstrumentationTestCase2; +import edu.vuum.mocca.DownloadActivity; +import edu.vuum.mocca.DownloadUtils; + +/** + * @class DownloadUtilsTests + * + * @brief Test the functionality of the DownloadUtils class + */ +public class DownloadUtilsTests + extends + ActivityInstrumentationTestCase2 { + /** + * Constructor initializes the superclass. + */ + public DownloadUtilsTests () { + super (DownloadActivity.class); + } + + // The intent returned by makeMessengerIntent() + Intent mIntent; + + // The bundle that is part of the intent + Bundle mExtras; + + /** + * Use this latch to wait for messages to be received by the + * Handler's thread. + */ + static CountDownLatch mLatch; + + /** + * Stores the Uri received by the Handler. + */ + static String mReceivedUri = null; + + /** + * The Handler that receives a Message reply from the + * IntentService. + */ + static MessageHandler mHandler; + + /** + * @class MessageHandler + * + * @brief Define a handler to catch messages from sendPath() + */ + static class MessageHandler extends Handler { + /** + * Constructor initializes the superclass + */ + public MessageHandler(Looper myLooper) { + super(myLooper); + } + + /** + * Handle return messages from the IntentService. Store the + * received Uri and notify the JUnit thread. + */ + public void handleMessage(Message msg) { + + // We don't know what tag they'll use for the path, so we + // have to search for it. + mReceivedUri = Utilities.searchForPath(msg); + + // Let the unit test thread know we've gotten a message. + mLatch.countDown(); + } + } + + /** + * This method is called before each test is run to perform + * initialization activities. + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + + // Make an arbitrary Messenger intent. + mIntent = DownloadUtils.makeMessengerIntent(getActivity(), + DownloadUtilsTests.class, + new MessageHandler(Looper.myLooper()), + Options.TEST_URI); + mExtras = mIntent.getExtras(); + } + + /** + * Try downloading a file. + */ + public void test_downloadFile () { + Context context = getInstrumentation().getContext(); + String result = DownloadUtils.downloadFile(getActivity(), + Uri.parse(Options.TEST_URI)); + + assertTrue(Utilities.checkDownloadedImage(context, result)); + } + + /** + * Check that the intent has a Messenger attached. + */ + public void test_makeMessengerIntent_Messenger_Extra () { + assertTrue(Utilities.checkMessenger(mIntent)); + } + + /** + * Check that the intent has the proper Uri attached + */ + public void test_makeMessengerIntent_Uri_Extra () { + assertTrue(Utilities.checkUri(mIntent)); + } + + /** + * Check that the intent will start the class we told it to + */ + public void test_makeMessengerIntent_Class () { + assertTrue(Utilities.checkClass(mIntent, DownloadUtilsTests.class)); + } + + /** + * Try sending a message using sendPath(). + */ + public void test_sendPath () throws Exception { + mLatch = new CountDownLatch(1); + + // Start a thread to catch the message from sendPath() + new Thread ( new Runnable () { + public void run() { + Looper.prepare(); + mHandler = new MessageHandler(Looper.myLooper()); + Looper.loop(); + } + }).start(); + + // Wait for the handler to instantiate + Thread.sleep(Options.SHORT_WAIT_TIME); + + // Send a message to ourselves + DownloadUtils.sendPath(Options.TEST_URI, new Messenger(mHandler)); + + // Wait for it to get here + mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS); + + // See if we got the message or timed out. + assertNotNull(mReceivedUri); + + // Check that the Uri is correct + assertTrue(mReceivedUri.equals(Options.TEST_URI)); + + // Other tests use this + mReceivedUri = null; + } + + /** + * Test that the downloadAndRespond method properly uses the other + * two methods in DownloadUtils. + */ + public void test_downloadAndRespond() throws Exception { + mLatch = new CountDownLatch(1); + + // Start a thread to handle messages we send to ourselves + new Thread ( new Runnable () { + public void run() { + Looper.prepare(); + mHandler = new MessageHandler(Looper.myLooper()); + Looper.loop(); + } + }).start(); + + // Wait for mHandler to instantiate + Thread.sleep(Options.SHORT_WAIT_TIME); + + // Download the image and send a message to ourselves + DownloadUtils.downloadAndRespond(getActivity(), + Uri.parse(Options.TEST_URI), + new Messenger(mHandler)); + + // Wait for it to get here. + mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS); + + // Check if we timed out or got the message + assertNotNull(mReceivedUri); + + // Make sure the image downloaded correctly. + assertTrue(Utilities.checkDownloadedImage(getInstrumentation().getContext(), mReceivedUri)); + + // Other tests use this + mReceivedUri = null; + } +} diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java new file mode 100644 index 000000000..042f8f648 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java @@ -0,0 +1,34 @@ +package edu.vuum.mocca.test; + +/** + * @class Options + * + * @brief Holds global constants for the testing suite. More + * convenient than grabbing resources from /res and trying to + * finagle a working Context out of the test classes every time + * we want to use TEST_URI. + * + */ +public class Options { + + /** + * The online URL of the image we're using for testing. + */ + static final String TEST_URI = "https://d396qusza40orc.cloudfront.net/posa/dougs-xsmall.jpg"; + + /** + * Whatever image we're testing, store it in res/drawable-nodpi so + * we can compare it to what's downloaded. + */ + static final int TEST_IMAGE = R.drawable.dougs; + + /** + * Time we should wait for things to instantiate. + */ + static final long SHORT_WAIT_TIME = 10000; + + /** + * Time we should wait for things to download. + */ + static final long LONG_WAIT_TIME = 25000; +} diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java new file mode 100644 index 000000000..c1cc6020a --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java @@ -0,0 +1,168 @@ +package edu.vuum.mocca.test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.test.ServiceTestCase; +import edu.vuum.mocca.DownloadUtils; +import edu.vuum.mocca.ThreadPoolDownloadService; + +/** + * @class ThreadPoolDownloadServiceTests + * + * @brief Tests the functionality of the ThreadPoolDownloadService. + */ +public class ThreadPoolDownloadServiceTests + extends + ServiceTestCase { + /** + * Use this latch to wait for messages to be received by the + * Handler's thread. + */ + static CountDownLatch mLatch; + + /** + * Stores the Uri received by the Handler. + */ + static String mReceivedUri = null; + + /** + * The Handler that receives a Message reply from the + * IntentService. + */ + static MessageHandler mHandler; + + /** + * Store the intent from makeIntent() to test proper creations. + */ + Intent mIntent; + + /** + * The context of THIS project, not the target project + */ + Context mContext; + + /** + * Constructor initializes the superclass. + */ + public ThreadPoolDownloadServiceTests() { + super(ThreadPoolDownloadService.class); + } + + /** + * @class MessageHandler + * + * @brief Define a private handler to catch messages from the service. + */ + static class MessageHandler extends Handler { + /** + * Constructor initializes the superclass + */ + public MessageHandler(Looper myLooper) { + super(myLooper); + } + + /** + * Handle return messages from the IntentService. Store the + * received Uri and notify the JUnit thread. + */ + public void handleMessage(Message msg) { + // We don't know what tag they'll use for the path, so we + // have to search for it. + mReceivedUri = Utilities.searchForPath(msg); + + // Let the unit test thread know we've gotten a message. + mLatch.countDown(); + } + } + + /** + * This is called once before each test is run. + */ + public void setUp() throws Exception{ + super.setUp(); + + // Make an intent using the factory method + mIntent = ThreadPoolDownloadService.makeIntent(getContext(), + new Handler(), + Options.TEST_URI); + + // Get the context for THIS project, not the target project. + mContext = getContext().createPackageContext(this.getClass().getPackage().getName(), + Context.CONTEXT_IGNORE_SECURITY); + + } + + /** + * Check that the intent will start the correct service. + */ + public void test_makeIntent_Class () { + assertTrue(Utilities.checkClass(mIntent, ThreadPoolDownloadService.class)); + } + + /** + * Check that the intent has the correct uri. + */ + public void test_makeIntent_Uri () { + assertTrue(Utilities.checkUri(mIntent)); + } + + /** + * Check that the intent has a Messenger attached. + */ + public void test_makeIntent_Messenger () { + assertTrue(Utilities.checkMessenger(mIntent)); + } + + /** + * Test starting the service. + */ + public void test_startService () throws Exception{ + + mLatch = new CountDownLatch(1); + + // Start a thread to handle the message when it's sent. + new Thread(new Runnable() { + public void run() { + Looper.prepare(); + mHandler = new MessageHandler(Looper.myLooper()); + Looper.loop(); + } + }).start(); + + // Wait for the handler to get instantiated + Thread.sleep(Options.SHORT_WAIT_TIME); + + //Create an Intent to start the service + mIntent = ThreadPoolDownloadService.makeIntent(getContext(), + mHandler, + Options.TEST_URI); + + // Start the service + startService(mIntent); + + // Check if the service actually started + assertNotNull(getService()); + + // Wait for the service to download and send us a message + mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS); + + // See if we timed out or actually got a message + assertNotNull(mReceivedUri); + + // Get the context for THIS project, not the target project + Context context = getContext().createPackageContext(this.getClass().getPackage().getName(), + Context.CONTEXT_IGNORE_SECURITY); + + // Check that the file downloaded correctly + assertTrue(Utilities.checkDownloadedImage(context, mReceivedUri)); + } +} + + diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java new file mode 100644 index 000000000..107d69d8e --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java @@ -0,0 +1,86 @@ +package edu.vuum.mocca.test; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import edu.vuum.mocca.DownloadUtils; + +/** + * @class Utilities + * + * @brief A few checks are repeated several times, such as checking + * whether a Messenger is present in an Intent, or if the proper + * Uri is set. + */ +public class Utilities { + /** + * Check that the proper Uri is set for the provided Intent. + */ + static boolean checkUri(Intent intent) { + return Options.TEST_URI.equals(intent.getData().toString()); + } + + /** + * Check that the provided Intent contains a Messenger extra. + */ + static boolean checkMessenger(Intent intent) { + Bundle extras = intent.getExtras(); + + // We don't know what tag they'll use for the Messenger, so we + // have to search for it. + Messenger messenger = null; + + for (String key : extras.keySet()) { + if (extras.get(key) instanceof Messenger) + messenger = (Messenger) extras.get(key); + } + + return (messenger != null); + } + + /** + * Check that the provided Intent will start the expected class. + */ + static boolean checkClass(Intent intent, Class class_) { + return intent.getComponent().getClassName().equals(class_.getName()); + } + + + /** + * Check if the downloaded Bitmap is equivalent to the expected + * bitmap. + */ + static boolean checkDownloadedImage(Context test_context, String result) { + Bitmap downloaded = BitmapFactory.decodeFile(result); + + Bitmap expected = BitmapFactory.decodeResource(test_context.getResources(), + Options.TEST_IMAGE); + return downloaded.sameAs(expected); + } + + /** + * Search for the pathname that should be bundled in the provided + * Message. (Returns the last String extra found). + */ + static String searchForPath (Message msg) { + Bundle msg_extras = msg.getData(); + + String path = null; + for (String key : msg_extras.keySet()) { + if (msg_extras.get(key) instanceof String) + path = (String) msg_extras.get(key); + } + + return path; + } + +} diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath new file mode 100644 index 000000000..980bef1db --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore new file mode 100644 index 000000000..102b6fc94 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore @@ -0,0 +1 @@ +*.properties \ No newline at end of file diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project new file mode 100644 index 000000000..7ec10d267 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project @@ -0,0 +1,33 @@ + + + W6-A5-ThreadedDownloads-StartedServices + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml new file mode 100644 index 000000000..33958ae4e --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar new file mode 100644 index 000000000..1931dd97f Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties new file mode 100644 index 000000000..a3ee5ab64 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..96a442e5b Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..359047dfa Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..71c6d760f Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png new file mode 100644 index 000000000..3a751e9cd Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png differ diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml new file mode 100644 index 000000000..0ae335689 --- /dev/null +++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + diff --git a/assignments/week-7-assignment-6/iRemember/res/layout/story_listview_custom_row.xml b/assignments/week-7-assignment-6/iRemember/res/layout/story_listview_custom_row.xml new file mode 100644 index 000000000..a0944cbd0 --- /dev/null +++ b/assignments/week-7-assignment-6/iRemember/res/layout/story_listview_custom_row.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assignments/week-7-assignment-6/iRemember/res/layout/view_story_activity.xml b/assignments/week-7-assignment-6/iRemember/res/layout/view_story_activity.xml new file mode 100644 index 000000000..87aefeb16 --- /dev/null +++ b/assignments/week-7-assignment-6/iRemember/res/layout/view_story_activity.xml @@ -0,0 +1,395 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +