Commits

unknown  committed a9cde2d

Added skeleton code to the repository.

  • Participants

Comments (0)

Files changed (27)

+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>BiddingServer-student</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

File .settings/org.eclipse.jdt.core.prefs

+#Sat Jan 21 10:44:10 EST 2012
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6

File bin/cmsc433/p1/Buyer.class

Binary file added.

File bin/cmsc433/p1/Client.class

Binary file added.

File bin/cmsc433/p1/Item.class

Binary file added.

File bin/cmsc433/p1/Seller.class

Binary file added.

File bin/cmsc433/p1/Server.class

Binary file added.

File bin/cmsc433/p1/tests/Logger.class

Binary file added.

File bin/cmsc433/p1/tests/Program.class

Binary file added.

File bin/cmsc433/p1/tests/ServerPrinter.class

Binary file added.

File bin/cmsc433/p1/tests/ServerTestHelper.class

Binary file added.

File bin/cmsc433/p1/tests/ServerTestMulti.class

Binary file added.

File bin/cmsc433/p1/tests/ServerTestSingleThread.class

Binary file added.

File bin/cmsc433/p1/tests/TestFileGenerator.class

Binary file added.

File src/cmsc433/p1/Buyer.java

+package cmsc433.p1;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Random;
+
+public class Buyer implements Client
+{
+    private String name;
+    private int cash;
+    private int cycles;
+    private int maxSleepTimeMs;
+    private int initialCash;
+
+    private Random rand;
+    
+    private Server server;
+    
+    private int mostItemsAvailable = 0;
+
+    public Buyer(Server server, String name, int cash, int cycles, int maxSleepTimeMs, long randomSeed)
+    {
+        this.name = name;
+        this.cash = cash;
+        this.cycles = cycles;
+        this.maxSleepTimeMs = maxSleepTimeMs;
+        this.initialCash = cash;
+
+        this.rand = new Random(randomSeed);
+        
+        this.server = server;
+    }
+    
+    public int cash()
+    {
+        return this.cash;
+    }
+    
+    public int cashSpent()
+    {
+        return this.initialCash - this.cash;
+    }
+    
+    public int mostItemsAvailable()
+    {
+        return this.mostItemsAvailable;
+    }
+
+    @Override
+    public String name()
+    {
+        return this.name;
+    }
+
+    @Override
+    public void run()
+    {
+        List<Item> activeBids = new ArrayList<Item>();
+        Hashtable<Item, Integer> activeBidPrices = new Hashtable<Item, Integer>();
+        int sumActiveBids = 0;
+
+        for (int i = 0; (i < cycles && cash > 0) || activeBids.size() > 0; ++i)
+        {
+            List<Item> items = server.getItems();
+            if (items.size() > this.mostItemsAvailable) { this.mostItemsAvailable = items.size(); }
+
+            while (items.size() > 0)
+            {
+                
+                int index = rand.nextInt(items.size());
+
+                Item item = items.get(index);
+                items.remove(index);
+
+                int price = server.itemPrice(item.listingID());
+                if (price < this.cash - sumActiveBids)
+                {
+                    // The server should ensure thread safety: if the price
+                    // has already increased, then this bid should be invalid.
+                    boolean success = server.submitBid(this.name(), item.listingID(), price + 1);
+
+                    if (success)
+                    {
+                        if (!activeBidPrices.containsKey(item))
+                        {
+                            activeBids.add(item);
+                        }
+                        else
+                        {
+                            sumActiveBids -= activeBidPrices.get(item);
+                        }
+                        
+                        sumActiveBids += price + 1;
+                        activeBidPrices.put(item, price + 1);
+                    }
+                    break;
+                }
+
+                continue;
+            }
+
+            List<Item> newActiveBids = new ArrayList<Item>();
+            Hashtable<Item, Integer> newActiveBidPrices = new Hashtable<Item, Integer>();
+            for (Item bid : activeBids)
+            {
+                switch (server.checkBidStatus(this.name(), bid.listingID()))
+                {
+                    case 1:
+                        // Success
+                        int finalPrice = activeBidPrices.get(bid);
+                        this.cash -= finalPrice;
+                        sumActiveBids -= finalPrice;
+
+                        break;
+
+                    case 2:
+                        // Open
+                        newActiveBids.add(bid);
+                        newActiveBidPrices.put(bid, activeBidPrices.get(bid));
+                        break;
+
+                    case 3:
+                        // Failed
+                        sumActiveBids -= activeBidPrices.get(bid);
+                        break;
+
+                    default:
+                        // Error
+                        break;
+                }
+            }
+
+            activeBids = newActiveBids;
+            activeBidPrices = newActiveBidPrices;
+            
+            try
+            {
+                Thread.sleep((long)rand.nextInt(this.maxSleepTimeMs));
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+    }
+
+}

File src/cmsc433/p1/Client.java

+package cmsc433.p1;
+
+public interface Client extends Runnable
+{
+	String name();
+}

File src/cmsc433/p1/Item.java

+package cmsc433.p1;
+
+import java.util.Date;
+
+
+/**
+ * Stores the initial information for an item submitted for bidding.
+ *
+ */
+public class Item
+{	
+    private String seller;
+    private String name;
+    private int listingID;
+    private int lowestBiddingPrice;
+    private int biddingDurationMs;
+    
+    private Date biddingStart;
+    
+	public Item(String seller, String name, int listingID, int lowestBiddingPrice, int biddingDurationMs)
+	{
+	    this.seller = seller;
+		this.name = name;
+		this.listingID = listingID;
+		this.lowestBiddingPrice = lowestBiddingPrice;
+		this.biddingDurationMs = biddingDurationMs;
+		
+		this.biddingStart = new Date();
+	}
+	
+	public String seller()
+	{
+	    return this.seller;
+	}
+	
+	public String name()
+	{
+		return this.name;
+	}
+	
+	public int listingID()
+	{
+		return this.listingID;		
+	}
+	
+	public int lowestBiddingPrice()
+	{
+	    return this.lowestBiddingPrice;
+	}
+	
+	public int biddingDurationMs()
+	{
+	    return this.biddingDurationMs;
+	}
+	
+	/**
+	 * Returns true if the bidding is open (active) for the current item.
+	 * 
+	 * In other words, if the time elapsed from the moment the bidding 
+	 * started for this item is less than the bidding duration, it will
+	 * return true. Otherwise it returns false. 
+	 */
+	public boolean biddingOpen()
+	{
+	    Date now = new Date();
+	    return (now.getTime() - this.biddingStart.getTime()) / 1 < this.biddingDurationMs;
+	}
+	
+	@Override
+	public boolean equals(Object obj)
+	{
+	    if (obj == null) { return false; }
+	    
+	    if (!obj.getClass().equals(this.getClass()))
+	    {
+	        return false;
+	    }
+	    
+	    Item item = (Item)obj;
+	    
+	    return item.listingID == this.listingID;
+	}
+	
+	@Override
+	public int hashCode()
+	{
+	    return this.listingID;
+	}
+	
+	@Override
+	public String toString()
+	{
+	    return ((Integer)this.listingID).toString();
+	}
+}

File src/cmsc433/p1/Seller.java

+package cmsc433.p1;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class Seller implements Client
+{
+	private static final int MaxItems = 100;
+	
+	private String name;
+	private int cycles;
+	private int maxSleepTimeMs;
+	
+	private List<String> items;
+	private Random rand;
+	
+	private Server server;
+	
+	public Seller(Server server, String name, int cycles, int maxSleepTimeMs, long randomSeed)
+	{
+		this.name = name;
+		this.cycles = cycles;
+		this.maxSleepTimeMs = maxSleepTimeMs;
+		
+		// Generate items
+		this.rand = new Random(randomSeed);
+        int itemCount = MaxItems;
+        this.items = new ArrayList<String>();
+        
+        for (int i = 0; i < itemCount; ++i)
+        {
+            items.add(this.name() + "#" + i);
+        }
+        
+        this.server = server;
+	}
+	
+	@Override
+	public String name()
+	{
+		return this.name;
+	}
+
+	@Override
+    public void run()
+    {			
+		for (int i = 0; i < this.cycles && this.items.size() > 0; ++i)
+	    {
+	    	int index = this.rand.nextInt(this.items.size());
+	    	String item = this.items.get(index);
+	    	
+	    	int listingID = server.submitItem(this.name(), item, this.rand.nextInt(100), this.rand.nextInt(100) + 100);
+	    	
+	    	if (listingID != -1)
+	    	{
+	    		this.items.remove(index);
+	    	}
+
+    		try
+            {
+                Thread.sleep(this.rand.nextInt(this.maxSleepTimeMs));
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+                return;
+            }
+	    }
+    }
+}

File src/cmsc433/p1/Server.java

+package cmsc433.p1;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Timer;
+
+public class Server
+{
+    /**
+     * Singleton: the following code makes the server a Singleton. You should
+     * not edit this code.
+     * 
+     * For test purposes, we made the constructor protected. 
+     */
+
+    /* Singleton: Begin */
+    protected Server()
+    {
+    }
+
+    private static Server instance = new Server();
+
+    public static Server getInstance()
+    {
+        return instance;
+    }
+
+    /* Singleton: End */
+    
+    /**
+     * Server statistics:
+     */
+    private int revenue = 0;
+    private int soldItemsCount = 0;
+
+    /**
+     * Server restrictions:
+     */
+    public static final int maxBidCount = 10; // The maximum number of bids at any given time for a buyer.
+    public static final int maxSellerItems = 20; // The maximum number of items that a seller can submit at any given time.
+    public static final int serverCapacity = 80; // The maximum number of active items at a given time.
+    
+    private Timer timer = new Timer(); // The timer is used to schedule the closing of a bid, after the given amount of time.
+    
+    private Hashtable<String, Integer> sellerProducts = new Hashtable<String, Integer>(); // Stores the number of active bids for each seller.
+
+    private int lastListingID = -1; // The last used listing ID assigned to an item. 
+    
+    // listingID, bid
+    private Hashtable<Integer, Integer> highestBids = new Hashtable<Integer, Integer>(); // Stores the highest bid for each item (includes expired bids).
+    
+    // listingID, buyer
+    private Hashtable<Integer, String> highestBidders = new Hashtable<Integer, String>(); // Stores the buyer which submitted the highest bid for each item.
+    
+    // buyer, bid count
+    private Hashtable<String, Integer> buyerBidCounts = new Hashtable<String, Integer>(); // Stores the number of active bids for each buyer.
+    
+    // listingID, item
+    private Hashtable<Integer, Item> items = new Hashtable<Integer, Item>(); // Stores a mapping between item IDs and items. (Includes expired items.)
+    
+    // listingID, item
+    private List<Item> openBids = new ArrayList<Item>();
+    
+    private Object instanceLock = new Object(); // Object used for instance synchronization. (As a good practice we don't use synchronized (this).)
+    
+    public int soldItemsCount()
+    {
+        return this.soldItemsCount;
+    }
+
+    public int revenue()
+    {
+        return this.revenue;
+    }
+
+    public int submitItem(String clientName, String itemName, int lowestBiddingPrice, int biddingDurationMs)
+    {
+        // TODO: Modify this code so that the project requirements are met, 
+        //       the unit tests pass, and the code is thread-safe.        
+        
+        if (this.openBids.size() == serverCapacity) 
+        { 
+            return -1; 
+        }
+        
+        Integer count = this.sellerProducts.get(clientName);
+        if (count == null)
+        {
+            count = 0;
+            this.sellerProducts.put(clientName, count);
+        }
+
+        if (count == maxSellerItems) { return -1; }
+
+        Item item = new Item(clientName, itemName, ++this.lastListingID, lowestBiddingPrice, biddingDurationMs);
+        this.items.put(this.lastListingID, item);
+        this.openBids.add(item);
+        this.highestBids.put(this.lastListingID, lowestBiddingPrice);
+        
+        return this.lastListingID;
+    }
+
+    public List<Item> getItems()
+    {
+        // TODO: Modify this code so that the project requirements are met, 
+        //       the unit tests pass, and the code is thread-safe.
+        
+        return this.openBids;
+    }
+
+    public boolean submitBid(String clientName, int listingID, int biddingAmount)
+    {
+        // TODO: Modify this code so that the project requirements are met, 
+        //       the unit tests pass, and the code is thread-safe.
+        
+        Item item = this.items.get(listingID);
+        if (!item.biddingOpen()) 
+        { 
+            // Bidding closed for this item
+            return false; 
+        }
+        
+        Integer highestBid = this.highestBids.get(listingID);
+        if (highestBid != null && highestBid >= biddingAmount)
+        {
+            // Bid amount is too small
+            return false;
+        }
+        
+        Integer buyerBidCount = this.buyerBidCounts.get(clientName); 
+        if (buyerBidCount != null && buyerBidCount >= maxBidCount)
+        {
+            // This buyer reached his limit of bids for different items
+            return false;
+        }
+        
+        if (buyerBidCount == null)
+        {
+            buyerBidCount = 0;
+        }
+        
+        this.buyerBidCounts.put(clientName, buyerBidCount+1);
+        
+        this.highestBids.put(listingID, biddingAmount);
+        this.highestBidders.put(listingID, clientName);
+        
+        return true;
+    }
+
+    public int checkBidStatus(String clientName, int listingID)
+    {
+        // TODO: Modify this code so that the project requirements are met, 
+        //       the unit tests pass, and the code is thread-safe.
+        
+        Item item = this.items.get(listingID);
+        
+        if (!item.biddingOpen())
+        {
+            String highestBidder = this.highestBidders.get(item.listingID());
+            
+            if (this.openBids.contains(item))
+            {
+                int price = this.highestBids.get(item.listingID());
+                // TODO: Update statistics here.
+                
+                this.openBids.remove(item);
+            }    
+            
+            // Update the number of open bids for this seller
+            int sellerProductsCount = this.sellerProducts.get(item.seller());
+            this.sellerProducts.put(item.seller(), sellerProductsCount-1);
+            
+            // Bidding for this item is over
+            if (highestBidder != null && highestBidder.equals(clientName))
+            {
+                // SUCCESS
+                return 1;
+            } 
+            else
+            {
+                // FAILED
+                return 3;
+            }
+        }
+        else
+        {
+            // OPEN
+            return 2;
+        }
+    }
+
+    public int itemPrice(int listingID)
+    {
+        Integer price = this.highestBids.get(listingID);
+        
+        if (price == null)
+        {
+            Item item = this.items.get(listingID);
+            price = item.lowestBiddingPrice();
+        }
+        
+        return price;
+    }
+}

File src/cmsc433/p1/tests/Logger.java

+package cmsc433.p1.tests;
+
+import java.io.PrintStream;
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * A helper class which prints the results of a bidding to an output stream.
+ * 
+ * By default, it used the standard output.
+ *
+ */
+public class Logger
+{
+    private static Logger instance = new Logger();
+    
+    public static Logger getInstance() { return instance; }
+    
+    private Logger() { }
+    
+    private PrintStream outStream = System.out;
+    
+    public void setOutput(PrintStream outStream)
+    {
+        this.outStream = outStream;
+    }
+    
+    public void logStart(String method, Object... args)
+    {
+        synchronized (this.outStream)
+        {
+            String argStr = "";
+            if (args.length > 0)
+            {
+                for (int i=0; i<args.length-1; ++i)
+                {
+                    argStr += args[i].toString() + ", ";
+                }
+                argStr += args[args.length-1].toString();
+            }
+            Date now = new Date();
+            
+            Format formatter = new SimpleDateFormat("HH:mm:ss.SSS");
+            this.outStream.format("S [%s] %s(%s)\n", formatter.format(now), method, argStr);
+            this.outStream.flush();
+        }
+    }
+    
+    public void logEnd(String method, Object returnValue, Object... args)
+    {
+        synchronized (this.outStream)
+        {
+            String argStr = "";
+            if (args.length > 0)
+            {
+                for (int i=0; i<args.length-1; ++i)
+                {
+                    argStr += args[i].toString() + ", ";
+                }
+                argStr += args[args.length-1].toString();
+            }
+            Date now = new Date();
+            
+            Format formatter = new SimpleDateFormat("HH:mm:ss.SSS");
+            if (returnValue == null) { returnValue = ""; }
+            this.outStream.format("E [%s] %s(%s) -> %s\n", formatter.format(now), method, argStr, returnValue.toString());
+            this.outStream.flush();
+        }
+    }
+}

File src/cmsc433/p1/tests/Program.java

+package cmsc433.p1.tests;
+
+import cmsc433.p1.*;
+
+/**
+ * Class provided for ease of test. This will not be used in the project 
+ * evaluation, so feel free to modify it as you like.
+ */ 
+public class Program
+{
+    public static void main(String[] args)
+    {        
+        // Use the following statements to redirect the log to a stream different from stdout:
+        // ByteArrayOutputStream os = new ByteArrayOutputStream();
+        // PrintStream ps = new PrintStream(os);
+        // Logger.getInstance().setOutput(ps);
+        
+        int nrSellers = 50;
+        int nrBuyers = 20;
+        
+        Thread[] sellerThreads = new Thread[nrSellers];
+        Thread[] buyerThreads = new Thread[nrBuyers];
+        Seller[] sellers = new Seller[nrSellers];
+        Buyer[] buyers = new Buyer[nrBuyers];
+        
+        for (int i=0; i<nrSellers; ++i)
+        {
+            sellers[i] = new Seller(ServerPrinter.getInstance(), "Seller"+i, 100, 50, i);
+            sellerThreads[i] = new Thread(sellers[i]);
+            sellerThreads[i].start();
+        }
+        
+        for (int i=0; i<nrBuyers; ++i)
+        {
+            buyers[i] = new Buyer(ServerPrinter.getInstance(), "Buyer"+i, 1000, 20, 150, i);
+            buyerThreads[i] = new Thread(buyers[i]);
+            buyerThreads[i].start();
+        }
+        
+        for (int i=0; i<nrSellers; ++i)
+        {
+            try
+            {
+                sellerThreads[i].join();
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        
+        int moneySpent = 0;
+        for (int i=0; i<nrBuyers; ++i)
+        {
+            try
+            {
+                buyerThreads[i].join();
+                moneySpent += buyers[i].cashSpent();
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        
+        // Use the following statements to redirect the log to a stream different from stdout:
+        // String output = os.toString();
+        // FileWriter writer;
+        // try
+        // {
+        //     // Output the log of the test in a separate file, for ease of debug.
+        //     writer = new FileWriter("out.txt");
+        //     writer.write(output);
+        //     writer.close();
+        // }
+        // catch (IOException e)
+        // {
+        //     e.printStackTrace();
+        // }
+
+        System.out.println("The following two numbers should be the same:");
+        System.out.println("The total sum of money spent, reported by te buyers is " + moneySpent);
+        System.out.println("The total revenue reported by the server is " + ServerPrinter.getInstance().revenue());
+        System.out.println();
+        
+        System.out.println("The following numbers may differ, but may not exceed 100:");
+        for (int i=0; i<buyers.length; ++i)
+        {
+            System.out.println("The most items available to bid for, recorded by " + buyers[i].name() + " is " + buyers[i].mostItemsAvailable());
+        }
+    }
+}

File src/cmsc433/p1/tests/ServerPrinter.java

+package cmsc433.p1.tests;
+
+import java.util.List;
+
+import cmsc433.p1.Item;
+import cmsc433.p1.Server;
+
+
+/**
+ * Wraps around the Server class, providing some logging for each method. 
+ * 
+ * This class is used for testing purposes.
+ *
+ */
+public class ServerPrinter extends Server
+{
+    private static ServerPrinter instance = new ServerPrinter();
+    
+    private ServerPrinter() { }
+    
+    public static Server getInstance() { return instance; }
+    
+    @Override
+    public int soldItemsCount()
+    {
+        Logger.getInstance().logStart("soldItemsCount");
+        
+        int returnValue = super.soldItemsCount();
+        Logger.getInstance().logEnd("soldItemsCount", returnValue);
+        
+        return returnValue;
+    }
+
+    @Override
+    public int revenue()
+    {
+        Logger.getInstance().logStart("revenue");
+        
+        int returnValue = super.revenue();
+        Logger.getInstance().logEnd("revenue", returnValue);
+        
+        return returnValue;
+    }
+
+    @Override
+    public int submitItem(String clientName, String itemName, int lowestBiddingPrice, int biddingDurationSeconds)
+    {
+        Logger.getInstance().logStart("submitItem", clientName, itemName, lowestBiddingPrice, biddingDurationSeconds);
+        
+        int returnValue = super.submitItem(clientName, itemName, lowestBiddingPrice, biddingDurationSeconds);
+        Logger.getInstance().logEnd("submitItem", returnValue, clientName, itemName, lowestBiddingPrice, biddingDurationSeconds);
+        
+        return returnValue;
+    }
+
+    @Override
+    public List<Item> getItems()
+    {
+        Logger.getInstance().logStart("getItems");
+        
+        List<Item> returnValue = super.getItems();
+        Logger.getInstance().logEnd("getItems", returnValue);
+        
+        return returnValue;
+    }
+
+    @Override
+    public boolean submitBid(String clientName, int listingID, int biddingAmount)
+    {
+        Logger.getInstance().logStart("submitBid", clientName, listingID, biddingAmount);
+        
+        boolean returnValue = super.submitBid(clientName, listingID, biddingAmount);
+        Logger.getInstance().logEnd("submitBid", returnValue, clientName, listingID, biddingAmount);
+        
+        return returnValue;
+    }
+
+    @Override
+    public int checkBidStatus(String clientName, int listingID)
+    {
+        Logger.getInstance().logStart("checkBidStatus", clientName, listingID);
+        
+        int returnValue = super.checkBidStatus(clientName, listingID);
+        Logger.getInstance().logEnd("checkBidStatus", returnValue, clientName, listingID);
+        
+        return returnValue;
+    }
+
+    @Override
+    public int itemPrice(int listingID)
+    {
+        Logger.getInstance().logStart("itemPrice", listingID);
+        
+        int returnValue = super.itemPrice(listingID);
+        Logger.getInstance().logEnd("itemPrice", returnValue, listingID);
+        
+        return returnValue;
+    }
+}

File src/cmsc433/p1/tests/ServerTestHelper.java

+package cmsc433.p1.tests;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import cmsc433.p1.Buyer;
+import cmsc433.p1.Seller;
+import cmsc433.p1.Server;
+
+public class ServerTestHelper
+{
+    public static void testMultiThread(int testNumber, int nrSellers, int nrBuyers)
+    {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(os);
+
+        Logger.getInstance().setOutput(ps);
+        
+        Thread[] sellerThreads = new Thread[nrSellers];
+        Thread[] buyerThreads = new Thread[nrBuyers];
+        Seller[] sellers = new Seller[nrSellers];
+        Buyer[] buyers = new Buyer[nrBuyers];
+        
+        for (int i=0; i<nrSellers; ++i)
+        {
+            Server serverPrinter = ServerPrinter.getInstance();
+            
+            sellers[i] = new Seller(
+                    /* server = */          serverPrinter, 
+                    /* sellerName = */      "Seller"+i, 
+                    /* cycles = */          100, 
+                    /* maxSleepTimeMs = */  50, 
+                    /* randomSeed = */      i);
+            sellerThreads[i] = new Thread(sellers[i]);
+            sellerThreads[i].start();
+        }
+        
+        for (int i=0; i<nrBuyers; ++i)
+        {
+            buyers[i] = new Buyer(
+                    /* server = */          ServerPrinter.getInstance(), 
+                    /* buyerName = */       "Buyer"+i, 
+                    /* initialCash = */     500, 
+                    /* cycles = */          20, 
+                    /* maxSleepTimeMs = */  150, 
+                    /* randomSeed = */      i);
+            buyerThreads[i] = new Thread(buyers[i]);
+            buyerThreads[i].start();
+        }
+        
+        for (int i=0; i<nrSellers; ++i)
+        {
+            try
+            {
+                sellerThreads[i].join();
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        
+        int moneySpent = 0;
+        for (int i=0; i<nrBuyers; ++i)
+        {
+            try
+            {
+                buyerThreads[i].join();
+                moneySpent += buyers[i].cashSpent();
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        
+        String output = os.toString();
+        
+        FileWriter writer;
+        try
+        {
+            // Output the log of the test in a separate file, for ease of debugging.
+            writer = new FileWriter("out" + testNumber + ".txt");
+            writer.write(output);
+            writer.close();
+        }
+        catch (IOException e)
+        {
+            e.printStackTrace();
+        }
+        
+        assertTrue("For test number " + testNumber + " the server revenue " + ServerPrinter.getInstance().revenue() + " differs from the revenue reported by the buyers " + moneySpent + "!", moneySpent == ServerPrinter.getInstance().revenue());
+        for (int i=0; i<buyers.length; ++i)
+        {
+            assertTrue("For test number " + testNumber + " the server items capacity " + buyers[i].mostItemsAvailable() + " exceeds the limit of 100!", buyers[i].mostItemsAvailable() <= 100);
+        }
+    }
+}

File src/cmsc433/p1/tests/ServerTestMulti.java

+package cmsc433.p1.tests;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import cmsc433.p1.*;
+
+public class ServerTestMulti
+{
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception
+    {
+    }
+
+    @AfterClass
+    public static void tearDownAfterClass() throws Exception
+    {
+    }
+
+    @Before
+    public void setUp() throws Exception
+    {
+        // Before each test, reset the Server, by re-initializing its instance. 
+        Constructor<ServerPrinter> serverConstructor = ServerPrinter.class.getDeclaredConstructor((Class<ServerPrinter>[])null);
+        serverConstructor.setAccessible(true);
+        
+        Field serverInstance = ServerPrinter.class.getDeclaredField("instance");
+        serverInstance.setAccessible(true);
+        
+        serverInstance.set(null, serverConstructor.newInstance((Object[])null));
+    }
+
+    @After
+    public void tearDown() throws Exception
+    {
+    }
+
+    private void testMultiThread(int testNumber, int nrSellers, int nrBuyers)
+    {   
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(os);
+
+        Logger.getInstance().setOutput(ps);
+        
+        Thread[] sellerThreads = new Thread[nrSellers];
+        Thread[] buyerThreads = new Thread[nrBuyers];
+        Seller[] sellers = new Seller[nrSellers];
+        Buyer[] buyers = new Buyer[nrBuyers];
+        
+        for (int i=0; i<nrSellers; ++i)
+        {
+            Server serverPrinter = ServerPrinter.getInstance();
+            
+            sellers[i] = new Seller(
+                    /* server = */          serverPrinter, 
+                    /* sellerName = */      "Seller"+i, 
+                    /* cycles = */          100, 
+                    /* maxSleepTimeMs = */  2, 
+                    /* randomSeed = */      i);
+            sellerThreads[i] = new Thread(sellers[i]);
+            sellerThreads[i].start();
+        }
+        
+        for (int i=0; i<nrBuyers; ++i)
+        {
+            buyers[i] = new Buyer(
+                    /* server = */          ServerPrinter.getInstance(), 
+                    /* buyerName = */       "Buyer"+i, 
+                    /* initialCash = */     1000, 
+                    /* cycles = */          20, 
+                    /* maxSleepTimeMs = */  2, 
+                    /* randomSeed = */      i);
+            buyerThreads[i] = new Thread(buyers[i]);
+            buyerThreads[i].start();
+        }
+        
+        for (int i=0; i<nrSellers; ++i)
+        {
+            try
+            {
+                sellerThreads[i].join();
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        
+        int moneySpent = 0;
+        for (int i=0; i<nrBuyers; ++i)
+        {
+            try
+            {
+                buyerThreads[i].join();
+                moneySpent += buyers[i].cashSpent();
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        
+//        String output = os.toString();
+//        
+//        FileWriter writer;
+//        try
+//        {
+//            // Output the log of the test in a separate file, for ease of debugging.
+//            writer = new FileWriter("out" + testNumber + ".txt");
+//            writer.write(output);
+//            writer.close();
+//        }
+//        catch (IOException e)
+//        {
+//            e.printStackTrace();
+//        }
+        
+        assertTrue("For test number " + testNumber + " the server revenue " + ServerPrinter.getInstance().revenue() + " differs from the revenue reported by the buyers " + moneySpent + "!", moneySpent == ServerPrinter.getInstance().revenue());
+        for (int i=0; i<buyers.length; ++i)
+        {
+            assertTrue("For test number " + testNumber + " the server items capacity " + buyers[i].mostItemsAvailable() + " exceeds the limit of " + Server.serverCapacity + "!", buyers[i].mostItemsAvailable() <= Server.serverCapacity);
+        }
+    }
+    
+    @Test
+    public void testMultiThread0()
+    {
+    testMultiThread(0, 80, 80);
+    }
+
+    @Test
+    public void testMultiThread1()
+    {
+    testMultiThread(1, 80, 160);
+    }
+
+    @Test
+    public void testMultiThread2()
+    {
+    testMultiThread(2, 80, 240);
+    }
+
+    @Test
+    public void testMultiThread3()
+    {
+    testMultiThread(3, 80, 320);
+    }
+
+    @Test
+    public void testMultiThread4()
+    {
+    testMultiThread(4, 80, 400);
+    }
+
+    @Test
+    public void testMultiThread5()
+    {
+    testMultiThread(5, 80, 480);
+    }
+
+    @Test
+    public void testMultiThread6()
+    {
+    testMultiThread(6, 80, 560);
+    }
+
+    @Test
+    public void testMultiThread7()
+    {
+    testMultiThread(7, 80, 640);
+    }
+
+    @Test
+    public void testMultiThread8()
+    {
+    testMultiThread(8, 80, 720);
+    }
+
+    @Test
+    public void testMultiThread9()
+    {
+    testMultiThread(9, 80, 800);
+    }
+
+    @Test
+    public void testMultiThread10()
+    {
+    testMultiThread(10, 160, 80);
+    }
+
+    @Test
+    public void testMultiThread11()
+    {
+    testMultiThread(11, 160, 160);
+    }
+
+    @Test
+    public void testMultiThread12()
+    {
+    testMultiThread(12, 160, 240);
+    }
+
+    @Test
+    public void testMultiThread13()
+    {
+    testMultiThread(13, 160, 320);
+    }
+
+    @Test
+    public void testMultiThread14()
+    {
+    testMultiThread(14, 160, 400);
+    }
+
+    @Test
+    public void testMultiThread15()
+    {
+    testMultiThread(15, 160, 480);
+    }
+
+    @Test
+    public void testMultiThread16()
+    {
+    testMultiThread(16, 160, 560);
+    }
+
+    @Test
+    public void testMultiThread17()
+    {
+    testMultiThread(17, 160, 640);
+    }
+
+    @Test
+    public void testMultiThread18()
+    {
+    testMultiThread(18, 160, 720);
+    }
+
+    @Test
+    public void testMultiThread19()
+    {
+    testMultiThread(19, 160, 800);
+    }
+
+    @Test
+    public void testMultiThread20()
+    {
+    testMultiThread(20, 240, 80);
+    }
+
+    @Test
+    public void testMultiThread21()
+    {
+    testMultiThread(21, 240, 160);
+    }
+
+    @Test
+    public void testMultiThread22()
+    {
+    testMultiThread(22, 240, 240);
+    }
+
+    @Test
+    public void testMultiThread23()
+    {
+    testMultiThread(23, 240, 320);
+    }
+
+    @Test
+    public void testMultiThread24()
+    {
+    testMultiThread(24, 240, 400);
+    }
+
+    @Test
+    public void testMultiThread25()
+    {
+    testMultiThread(25, 240, 480);
+    }
+
+    @Test
+    public void testMultiThread26()
+    {
+    testMultiThread(26, 240, 560);
+    }
+
+    @Test
+    public void testMultiThread27()
+    {
+    testMultiThread(27, 240, 640);
+    }
+
+    @Test
+    public void testMultiThread28()
+    {
+    testMultiThread(28, 240, 720);
+    }
+
+    @Test
+    public void testMultiThread29()
+    {
+    testMultiThread(29, 240, 800);
+    }
+
+    @Test
+    public void testMultiThread30()
+    {
+    testMultiThread(30, 320, 80);
+    }
+
+    @Test
+    public void testMultiThread31()
+    {
+    testMultiThread(31, 320, 160);
+    }
+
+    @Test
+    public void testMultiThread32()
+    {
+    testMultiThread(32, 320, 240);
+    }
+
+    @Test
+    public void testMultiThread33()
+    {
+    testMultiThread(33, 320, 320);
+    }
+
+    @Test
+    public void testMultiThread34()
+    {
+    testMultiThread(34, 320, 400);
+    }
+
+    @Test
+    public void testMultiThread35()
+    {
+    testMultiThread(35, 320, 480);
+    }
+
+    @Test
+    public void testMultiThread36()
+    {
+    testMultiThread(36, 320, 560);
+    }
+
+    @Test
+    public void testMultiThread37()
+    {
+    testMultiThread(37, 320, 640);
+    }
+
+    @Test
+    public void testMultiThread38()
+    {
+    testMultiThread(38, 320, 720);
+    }
+
+    @Test
+    public void testMultiThread39()
+    {
+    testMultiThread(39, 320, 800);
+    }
+
+    @Test
+    public void testMultiThread40()
+    {
+    testMultiThread(40, 400, 80);
+    }
+
+    @Test
+    public void testMultiThread41()
+    {
+    testMultiThread(41, 400, 160);
+    }
+
+    @Test
+    public void testMultiThread42()
+    {
+    testMultiThread(42, 400, 240);
+    }
+
+    @Test
+    public void testMultiThread43()
+    {
+    testMultiThread(43, 400, 320);
+    }
+
+    @Test
+    public void testMultiThread44()
+    {
+    testMultiThread(44, 400, 400);
+    }
+
+    @Test
+    public void testMultiThread45()
+    {
+    testMultiThread(45, 400, 480);
+    }
+
+    @Test
+    public void testMultiThread46()
+    {
+    testMultiThread(46, 400, 560);
+    }
+
+    @Test
+    public void testMultiThread47()
+    {
+    testMultiThread(47, 400, 640);
+    }
+
+    @Test
+    public void testMultiThread48()
+    {
+    testMultiThread(48, 400, 720);
+    }
+
+    @Test
+    public void testMultiThread49()
+    {
+    testMultiThread(49, 400, 800);
+    }
+
+    @Test
+    public void testMultiThread50()
+    {
+    testMultiThread(50, 480, 80);
+    }
+
+    @Test
+    public void testMultiThread51()
+    {
+    testMultiThread(51, 480, 160);
+    }
+
+    @Test
+    public void testMultiThread52()
+    {
+    testMultiThread(52, 480, 240);
+    }
+
+    @Test
+    public void testMultiThread53()
+    {
+    testMultiThread(53, 480, 320);
+    }
+
+    @Test
+    public void testMultiThread54()
+    {
+    testMultiThread(54, 480, 400);
+    }
+
+    @Test
+    public void testMultiThread55()
+    {
+    testMultiThread(55, 480, 480);
+    }
+
+    @Test
+    public void testMultiThread56()
+    {
+    testMultiThread(56, 480, 560);
+    }
+
+    @Test
+    public void testMultiThread57()
+    {
+    testMultiThread(57, 480, 640);
+    }
+
+    @Test
+    public void testMultiThread58()
+    {
+    testMultiThread(58, 480, 720);
+    }
+
+    @Test
+    public void testMultiThread59()
+    {
+    testMultiThread(59, 480, 800);
+    }
+
+    @Test
+    public void testMultiThread60()
+    {
+    testMultiThread(60, 560, 80);
+    }
+
+    @Test
+    public void testMultiThread61()
+    {
+    testMultiThread(61, 560, 160);
+    }
+
+    @Test
+    public void testMultiThread62()
+    {
+    testMultiThread(62, 560, 240);
+    }
+
+    @Test
+    public void testMultiThread63()
+    {
+    testMultiThread(63, 560, 320);
+    }
+
+    @Test
+    public void testMultiThread64()
+    {
+    testMultiThread(64, 560, 400);
+    }
+
+    @Test
+    public void testMultiThread65()
+    {
+    testMultiThread(65, 560, 480);
+    }
+
+    @Test
+    public void testMultiThread66()
+    {
+    testMultiThread(66, 560, 560);
+    }
+
+    @Test
+    public void testMultiThread67()
+    {
+    testMultiThread(67, 560, 640);
+    }
+
+    @Test
+    public void testMultiThread68()
+    {
+    testMultiThread(68, 560, 720);
+    }
+
+    @Test
+    public void testMultiThread69()
+    {
+    testMultiThread(69, 560, 800);
+    }
+
+    @Test
+    public void testMultiThread70()
+    {
+    testMultiThread(70, 640, 80);
+    }
+
+    @Test
+    public void testMultiThread71()
+    {
+    testMultiThread(71, 640, 160);
+    }
+
+    @Test
+    public void testMultiThread72()
+    {
+    testMultiThread(72, 640, 240);
+    }
+
+    @Test
+    public void testMultiThread73()
+    {
+    testMultiThread(73, 640, 320);
+    }
+
+    @Test
+    public void testMultiThread74()
+    {
+    testMultiThread(74, 640, 400);
+    }
+
+    @Test
+    public void testMultiThread75()
+    {
+    testMultiThread(75, 640, 480);
+    }
+
+    @Test
+    public void testMultiThread76()
+    {
+    testMultiThread(76, 640, 560);
+    }
+
+    @Test
+    public void testMultiThread77()
+    {
+    testMultiThread(77, 640, 640);
+    }
+
+    @Test
+    public void testMultiThread78()
+    {
+    testMultiThread(78, 640, 720);
+    }
+
+    @Test
+    public void testMultiThread79()
+    {
+    testMultiThread(79, 640, 800);
+    }
+
+    @Test
+    public void testMultiThread80()
+    {
+    testMultiThread(80, 720, 80);
+    }
+
+    @Test
+    public void testMultiThread81()
+    {
+    testMultiThread(81, 720, 160);
+    }
+
+    @Test
+    public void testMultiThread82()
+    {
+    testMultiThread(82, 720, 240);
+    }
+
+    @Test
+    public void testMultiThread83()
+    {
+    testMultiThread(83, 720, 320);
+    }
+
+    @Test
+    public void testMultiThread84()
+    {
+    testMultiThread(84, 720, 400);
+    }
+
+    @Test
+    public void testMultiThread85()
+    {
+    testMultiThread(85, 720, 480);
+    }
+
+    @Test
+    public void testMultiThread86()
+    {
+    testMultiThread(86, 720, 560);
+    }
+
+    @Test
+    public void testMultiThread87()
+    {
+    testMultiThread(87, 720, 640);
+    }
+
+    @Test
+    public void testMultiThread88()
+    {
+    testMultiThread(88, 720, 720);
+    }
+
+    @Test
+    public void testMultiThread89()
+    {
+    testMultiThread(89, 720, 800);
+    }
+
+    @Test
+    public void testMultiThread90()
+    {
+    testMultiThread(90, 800, 80);
+    }
+
+    @Test
+    public void testMultiThread91()
+    {
+    testMultiThread(91, 800, 160);
+    }
+
+    @Test
+    public void testMultiThread92()
+    {
+    testMultiThread(92, 800, 240);
+    }
+
+    @Test
+    public void testMultiThread93()
+    {
+    testMultiThread(93, 800, 320);
+    }
+
+    @Test
+    public void testMultiThread94()
+    {
+    testMultiThread(94, 800, 400);
+    }
+
+    @Test
+    public void testMultiThread95()
+    {
+    testMultiThread(95, 800, 480);
+    }
+
+    @Test
+    public void testMultiThread96()
+    {
+    testMultiThread(96, 800, 560);
+    }
+
+    @Test
+    public void testMultiThread97()
+    {
+    testMultiThread(97, 800, 640);
+    }
+
+    @Test
+    public void testMultiThread98()
+    {
+    testMultiThread(98, 800, 720);
+    }
+
+    @Test
+    public void testMultiThread99()
+    {
+    testMultiThread(99, 800, 800);
+    }
+}

File src/cmsc433/p1/tests/ServerTestSingleThread.java

+package cmsc433.p1.tests;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import cmsc433.p1.*;
+
+public class ServerTestSingleThread
+{
+
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception
+    {
+    }
+
+    @AfterClass
+    public static void tearDownAfterClass() throws Exception
+    {
+    }
+
+    @Before
+    public void setUp() throws Exception
+    {
+        // Before each test, reset the Server, by re-initializing its instance.
+        Constructor<Server> serverConstructor = Server.class.getDeclaredConstructor((Class<Server>[])null);
+        serverConstructor.setAccessible(true);
+        
+        Field serverInstance = Server.class.getDeclaredField("instance");
+        serverInstance.setAccessible(true);
+        
+        serverInstance.set(null, serverConstructor.newInstance((Object[])null));
+    }
+
+    @After
+    public void tearDown() throws Exception
+    {
+    }
+
+    @Test
+    public void testMaxCounts()
+    {
+        Server server = Server.getInstance();
+
+        assertEquals(0, server.getItems().size());
+        
+        for (int j=0; j<20; ++j)
+        {
+            for (int i=0; i<100; ++i)
+            {
+                server.submitItem("Client"+j, "Item" + i, 1, 5);
+            }
+            
+            List<Item> items = server.getItems();
+            assertEquals(Math.min(Server.maxSellerItems*(j+1), Server.serverCapacity), items.size());
+        }
+    }
+    
+    @Test
+    public void testUniqueIDs()
+    {
+        Server server = Server.getInstance();
+                
+        assertEquals(0, server.getItems().size());
+        
+        HashSet<Integer> ids = new HashSet<Integer>();
+        
+        for (int j=0; j<20; ++j)
+        {
+            for (int i=0; i<Server.maxSellerItems*10; ++i)
+            {
+                int id = server.submitItem("Client"+j, "Item" + i, 1, 5);
+                
+                if (i>=Server.maxSellerItems || j>=Server.serverCapacity / Server.maxSellerItems)
+                {
+                    assertEquals(-1, id);
+                }
+                else
+                {
+                    assertFalse(ids.contains(id));
+                    ids.add(id);
+                }
+            }
+        }
+    }
+    
+    @Test
+    public void testBiddingOpen()
+    {
+        Server server = Server.getInstance();
+        
+        assertEquals(0, server.getItems().size());
+        
+        List<Integer> ids = new ArrayList<Integer>();
+        
+        int sellers = Server.serverCapacity / Server.maxSellerItems;
+        for (int j=0; j<sellers; ++j)
+        {
+            for (int i=0; i<Server.maxSellerItems; ++i)
+            {
+                int id = server.submitItem("Client"+j, "Item" + i, 1, 5000);
+                
+                ids.add(id);
+            }
+        }
+        
+        for (Integer id : ids)
+        {
+            assertEquals(2, server.checkBidStatus("Buyer", id));
+        }
+    }
+    
+    @Test
+    public void testBiddingFailed()
+    {
+        Server server = Server.getInstance();
+        
+        assertEquals(0, server.getItems().size());
+        
+        List<Integer> ids = new ArrayList<Integer>();
+        
+        int sellers = Server.serverCapacity / Server.maxSellerItems;
+        for (int j=0; j<sellers; ++j)
+        {
+            for (int i=0; i<Server.maxSellerItems; ++i)
+            {
+                int id = server.submitItem("Client"+j, "Item" + i, 1, 1);
+                
+                ids.add(id);
+            }
+        }
+        
+        try
+        {
+            Thread.sleep(5);
+        }
+        catch (InterruptedException e)
+        {
+            e.printStackTrace();
+        }
+        
+        for (Integer id : ids)
+        {
+            assertEquals(3, server.checkBidStatus("Buyer", id));
+        }
+    }
+    
+    @Test
+    public void testBiddingSuccess()
+    {
+        Server server = Server.getInstance();
+        
+        assertEquals(0, server.getItems().size());
+        
+        List<Integer> ids = new ArrayList<Integer>();
+        
+        int sellers = Server.serverCapacity / Server.maxSellerItems;
+        for (int j=0; j<sellers; ++j)
+        {
+            for (int i=0; i<Server.maxSellerItems; ++i)
+            {
+                int id = server.submitItem("Client"+j, "Item" + i, 1, 10);
+                
+                ids.add(id);
+            }
+        }
+        
+        for (Integer id : ids)
+        {
+            server.submitBid("Buyer" + id, id, 2);
+        }
+        
+        try
+        {
+            Thread.sleep(50);
+        }
+        catch (InterruptedException e)
+        {
+            e.printStackTrace();
+        }
+        
+        for (Integer id : ids)
+        {
+            assertEquals(1, server.checkBidStatus("Buyer" + id, id));
+        }
+    }
+    
+    @Test
+    public void testBiddingLimits()
+    {
+        Server server = Server.getInstance();
+        
+        assertEquals(0, server.getItems().size());
+        
+        List<Integer> ids = new ArrayList<Integer>();
+        
+        int sellers = Server.serverCapacity/Server.maxSellerItems;
+        for (int j=0; j<sellers; ++j)
+        {
+            for (int i=0; i<Server.maxSellerItems; ++i)
+            {
+                int id = server.submitItem("Client"+j, "Item" + i, 1, 10);
+                
+                ids.add(id);
+            }
+        }
+        
+        for (int j=0; j<10; ++j)
+        {
+            int id = server.submitItem("Client"+j, "ItemInfinity", 1, 1);
+            
+            assertEquals(-1, id);
+        }
+        
+        for (int i=0; i<ids.size(); ++i)
+        {
+            int id = ids.get(i);
+            server.submitBid("Buyer1", id, 3);
+            server.submitBid("Buyer2", id, 1);
+            if (i>=2)
+            {
+                server.submitBid("Buyer3", id, 2);
+            }
+        }
+        
+        try
+        {
+            Thread.sleep(50);
+        }
+        catch (InterruptedException e)
+        {
+            e.printStackTrace();
+        }
+        
+        for (int i=0; i<ids.size(); ++i)
+        {
+            if (i<Server.maxBidCount)
+            {
+                assertEquals(1, server.checkBidStatus("Buyer1", ids.get(i)));
+            }
+            else
+            {
+                assertEquals(3, server.checkBidStatus("Buyer1", ids.get(i)));
+            }
+            
+            if (i>=Server.maxBidCount && i<Server.maxBidCount*2)
+            {
+                assertEquals(1, server.checkBidStatus("Buyer3", ids.get(i)));
+            }
+            else
+            {
+                assertEquals(3, server.checkBidStatus("Buyer3", ids.get(i)));
+            }
+            
+            assertEquals(3, server.checkBidStatus("Buyer2", ids.get(i)));
+        }
+        
+        ids.clear();
+        for (int j=0; j<sellers; ++j)
+        {
+            for (int i=0; i<Server.maxSellerItems; ++i)
+            {