Skip to content

Instantly share code, notes, and snippets.

@Jules-Baratoux
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jules-Baratoux/39cd12d2367fdbb3a5cf to your computer and use it in GitHub Desktop.
Save Jules-Baratoux/39cd12d2367fdbb3a5cf to your computer and use it in GitHub Desktop.
Project #1: Vending Machine
/*
* Vending Machine Project
* CSE-40477
*
* Coin.cpp
* YOU MUST IMPLEMENT THE FUNCTIONS IN THIS FILE.
*/
#include "Coin.h"
using Project1::Coin;
Coin::Coin(CoinType denomination)
: denomination(denomination)
{
// TODO: Implement
}
Coin::CoinType Coin::getDenomination() const
{
return denomination;
}
unsigned Coin::getValueCents() const
{
return static_cast<unsigned>(denomination);
}
/*
* Vending Machine Project
* CSE-40477
*
* Coin.h
* YOU MUST NOT CHANGE THIS FILE.
*/
#ifndef PROJECT1_COIN_H
#define PROJECT1_COIN_H
namespace Project1
{
//=========================================================================
// SUMMARY
// Represents coins up to a dollar in value. Includes wooden nickel
// that represents a bad coin that is worth nothing.
//
// RESOURCES
// A coin does not own any resources.
//=========================================================================
class Coin
{
public:
//---------------------------------------------------------------------
// Types of coins. The enumerator's numeric value is the coin's value
// in cents.
//---------------------------------------------------------------------
enum CoinType
{
COINTYPE_WOODEN_NICKEL = 0,
COINTYPE_PENNY = 1,
COINTYPE_NICKEL = 5,
COINTYPE_DIME = 10,
COINTYPE_QUARTER = 25,
COINTYPE_HALF_DOLLAR = 50,
COINTYPE_DOLLAR = 100
};
//---------------------------------------------------------------------
// SUMMARY
// Constructor. Initializes the coin's denomination to the given
// type.
//
// RESOURCES
// None
//
// PARAMETERS
// denomination
// The coin's denomination.
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
Coin(CoinType denomination);
//---------------------------------------------------------------------
// SUMMARY
// Returns the type of this coin.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// The type of this coin.
//---------------------------------------------------------------------
CoinType getDenomination() const;
//---------------------------------------------------------------------
// SUMMARY
// Returns the value of the coin in cents.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// The value of the coin in cents.
//---------------------------------------------------------------------
unsigned getValueCents() const;
private:
CoinType denomination;
};
}
#endif
/*
* Vending Machine Project
* CSE-40477
*
* DeliveryChute.cpp
* YOU MUST IMPLEMENT THE FUNCTIONS IN THIS FILE.
*/
#include "Product.h"
using Project1::Product;
#include "StatusPanel.h"
using Project1::StatusPanel;
#include "DeliveryChute.h"
using Project1::DeliveryChute;
DeliveryChute::DeliveryChute(StatusPanel &statusPanel)
: statusPanel(statusPanel),
pProduct(NULL)
{
}
DeliveryChute::~DeliveryChute()
{
delete pProduct;
}
bool DeliveryChute::insertProduct(Product *product)
{
if (pProduct == NULL)
{
pProduct = product;
return true;
}
else
{
statusPanel.displayMessage(StatusPanel::MESSAGECODE_CHUTE_FULL);
return false;
}
}
Product* DeliveryChute::retrieveProduct()
{
Product* ret = pProduct;
pProduct = NULL;
return ret;
}
bool DeliveryChute::containsProduct() const
{
return pProduct;
}
/*
* Vending Machine Project
* CSE-40477
*
* DeliveryChute.h
* YOU MUST NOT CHANGE THIS FILE.
*/
#ifndef PROJECT1_DELIVERYCHUTE_H
#define PROJECT1_DELIVERYCHUTE_H
#include "Product.h"
#include "StatusPanel.h"
namespace Project1
{
//=========================================================================
// SUMMARY
// The chute from which the customer can retrieve a purchased product.
// The chute takes ownership of any product dropped into the chute.
// The chute releases ownership of a product when the product iS
// retrieved from the chute.
//
// RESOURCES
// A delivery chute owns (i.e. is responsible for freeing the memory
// of) the product that is contained within the chute. The delivery
// chute relinquishes ownership of the product when the product is
// retrieved from the chute.
//=========================================================================
class DeliveryChute
{
public:
//---------------------------------------------------------------------
// SUMMARY
// Constructor. Creates an empty delivery chute.
//
// RESOURCES
// The chute is initialized to not own a product.
//
// PARAMETERS
// statusPanel
// The status panel to which messages will be output.
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
DeliveryChute(StatusPanel &statusPanel);
//---------------------------------------------------------------------
// SUMMARY
// Destructor. Destroys the delivery chute.
//
// RESOURCES
// The owned product is destroyed.
//
// PARAMETERS
// None
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
~DeliveryChute();
//---------------------------------------------------------------------
// SUMMARY
// Places the given product in the chute if the chute is currently
// empty. If the chute is not empty MESSAGECODE_CHUTE_FULL will
// be output to the status panel. Returns whether the product was
// successfully inserted into the chute.
//
// RESOURCES
// The chute takes ownership of the product if and only if the
// product was successfully added to the chute.
//
// PARAMETERS
// pProduct
// The product to be inserted into the chute.
//
// RETURNS
// Whether the product was successfully placed in the chute.
//---------------------------------------------------------------------
bool insertProduct(Product *pProduct);
//---------------------------------------------------------------------
// SUMMARY
// Returns the product currently in the chute.
//
// RESOURCES
// Ownership of the product is relinquished.
//
// PARAMETERS
// None
//
// RETURNS
// The product from the chute or 0 if no product is in the chute.
//---------------------------------------------------------------------
Product *retrieveProduct();
private:
//---------------------------------------------------------------------
// SUMMARY
// Returns whether the chute currently contains a product waiting
// to be retrieved.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// Whether the chute currently contains a product waiting to be
// retrieved.
//---------------------------------------------------------------------
bool containsProduct() const;
StatusPanel statusPanel;
Product *pProduct;
};
}
#endif
/*
* Vending Machine Project
* CSE-40477
*
* main.cpp
* YOU MUST NOT CHANGE THIS FILE.
*/
// Uncomment this section and the section at the start of main to dump a memory
// leak report at program terimination when using Visual Studio.
// See http://msdn.microsoft.com/en-us/library/x98tx3cf.aspx for more info.
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
#include "UnitTest.h"
int main()
{
cout << "-------------------------------------------------------------------------------" << endl
<< __TIME__ << endl
<< "-------------------------------------------------------------------------------" << endl;
// Uncomment this section and the section at the start of this file to
// dump a memory leak report at program termination when using Visual
// Studio.
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
// Uncomment this section to set a memory-allocation breakpoint at the
// given memory allocation number when using Visual Studio.
// _CrtSetBreakAlloc(458);
// Initialize
Project1UnitTest::Initialize(cout);
// Run tests
Project1UnitTest::TestCoin();
Project1UnitTest::TestProduct();
Project1UnitTest::TestDeliveryChute();
Project1UnitTest::TestProductRack();
Project1UnitTest::TestProductButton();
Project1UnitTest::TestStatusPanel();
Project1UnitTest::TestVendingMachine();
// Display test results
Project1UnitTest::DisplaySummary();
// Wait for user response
cout << endl << "Press enter to continue...";
cin.get();
}
/*
* Vending Machine Project
* CSE-40477
*
* Product.cpp
* YOU MUST IMPLEMENT THE FUNCTIONS IN THIS FILE.
*/
#include <cstring>
using std::strncpy;
#include "Product.h"
using Project1::Product;
Product::Product(const char *brand, const char *name, const char *size)
{
setBrand(brand);
setName(name);
setSize(size);
}
const char * Product::getBrand() const
{
return brand;
}
void Product::setBrand(const char *value)
{
strncpy(brand, value, MAX_BRAND_LENGTH);
}
const char * Product::getName() const
{
return name;
}
void Product::setName(const char *value)
{
strncpy(name, value, MAX_NAME_LENGTH);
}
const char * Product::getSize() const
{
return size;
}
void Product::setSize(const char *value)
{
strncpy(size, value, MAX_SIZE_LENGTH);
}
/*
* Vending Machine Project
* CSE-40477
*
* Product.h
* YOU MUST NOT CHANGE THIS FILE.
*/
#ifndef PROJECT1_PRODUCT_H
#define PROJECT1_PRODUCT_H
namespace Project1
{
//=========================================================================
// SUMMARY
// A product (soda) having the given brand, name, and size. Example
// brands include "Pepsi Cola" and "Coca Cola". Example names include
// "Pepsi" and "Coke". Example sizes include "12 fl oz".
//
// RESOURCES
// A product does not own any resources.
//=========================================================================
class Product
{
public:
static const int MAX_BRAND_LENGTH = 25;
static const int MAX_NAME_LENGTH = 25;
static const int MAX_SIZE_LENGTH = 25;
//---------------------------------------------------------------------
// SUMMARY
// Constructor. Creates a product of the given brand, name, and
// size. The name identifies the product; two products with the
// same name are considered to be the same product type.
//
// RESOURCES
// None
//
// PARAMETERS
// brand
// The product's brand. It is safe to assume brand will
// never be longer than MAX_BRAND_LENGTH.
// name
// The product's name. This uniquely identifies the product;
// two products with the same name are considered to be the
// same product type. It is safe to assume name will never
// be longer than MAX_NAME_LENGTH.
// size
// The product's size. It is safe to assume size will never
// be longer than MAX_SIZE_LENGTH.
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
Product(const char *brand, const char *name, const char *size);
//---------------------------------------------------------------------
// SUMMARY
// Get and set the product's brand.
//
// RESOURCES
// None
//
// PARAMETERS
// brand
// The product's brand (e.g. Coca-Cola).
//
// RETURNS
// The product's brand.
//---------------------------------------------------------------------
const char *getBrand() const;
void setBrand(const char *value);
//---------------------------------------------------------------------
// SUMMARY
// Get and set the product's name.
//
// RESOURCES
// None
//
// PARAMETERS
// name
// The product's name (e.g. Coke).
//
// RETURNS
// The product's name.
//---------------------------------------------------------------------
const char *getName() const;
void setName(const char *value);
//---------------------------------------------------------------------
// SUMMARY
// Get and set the product's size.
//
// RESOURCES
// None
//
// PARAMETERS
// size
// The product's size (e.g. 12 fl. oz.).
//
// RETURNS
// The product's size.
//---------------------------------------------------------------------
const char *getSize() const;
void setSize(const char *value);
private:
char brand[MAX_BRAND_LENGTH];
char name[MAX_NAME_LENGTH];
char size[MAX_SIZE_LENGTH];
};
}
#endif
/*
* Vending Machine Project
* CSE-40477
*
* ProductButton.cpp
* YOU MUST IMPLEMENT THE FUNCTIONS IN THIS FILE.
*/
#include <cstring>
using std::strcpy;
#include "ProductButton.h"
using Project1::ProductButton;
#include "ProductRack.h"
using Project1::ProductRack;
ProductButton::ProductButton(ProductRack &productRack)
: productRack(productRack)
{
}
bool ProductButton::press()
{
return productRack.deliverProduct();
}
unsigned ProductButton::getProductPriceCents() const
{
return productRack.getProductPriceCents();
}
/*
* Vending Machine Project
* CSE-40477
*
* ProductButton.h
* YOU MUST NOT CHANGE THIS FILE.
*/
#ifndef PROJECT1_PRODUCTBUTTON_H
#define PROJECT1_PRODUCTBUTTON_H
#include "Product.h"
#include "ProductRack.h"
namespace Project1
{
//=========================================================================
// SUMMARY
// A button associated with a product rack. Pressing the button
// will cause the associated product rack to deliver one of its
// products.
//
// RESOURCES
// A product button does not own any resources.
//=========================================================================
class ProductButton
{
public:
//---------------------------------------------------------------------
// SUMMARY
// Constructor. Creates a product button associated with the
// given product rack. When the button is pressed the given
// product rack will deliver one of its products.
//
// RESOURCES
// None
//
// PARAMETERS
// productRack
// The product rack that will deliver a product when the
// button is pressed.
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
ProductButton(ProductRack &productRack);
//---------------------------------------------------------------------
// SUMMARY
// Causes the associated product rack to deliver its next available
// product.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// Whether the product rack was able to deliver a product.
//---------------------------------------------------------------------
bool press();
//---------------------------------------------------------------------
// SUMMARY
// Returns the price of products in the associated product rack.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// The price of products in the associated product rack.
//---------------------------------------------------------------------
unsigned getProductPriceCents() const;
private:
ProductRack &productRack;
};
}
#endif
/*
* Vending Machine Project
* CSE-40477
*
* ProductRack.cpp
* YOU MUST IMPLEMENT THE FUNCTIONS IN THIS FILE.
*/
#include <cstring>
using std::strcmp;
using std::strncpy;
#include "utils.h"
#include "Product.h"
using Project1::Product;
#include "DeliveryChute.h"
using Project1::DeliveryChute;
#include "ProductRack.h"
using Project1::ProductRack;
#include "StatusPanel.h"
using Project1::StatusPanel;
ProductRack::ProductRack(
StatusPanel &statusPanel,
const char *allowedProductName,
DeliveryChute &deliveryChute,
unsigned productPriceCents)
: statusPanel(statusPanel),
deliveryChute(deliveryChute),
productCount(0),
productPriceCents(productPriceCents)
{
strncpy(this->allowedProductName, allowedProductName, Product::MAX_NAME_LENGTH);
}
ProductRack::~ProductRack()
{
for (size_t i = 0; i < productCount; i++)
{
delete products[i];
}
}
bool ProductRack::isCompatibleProduct(const char *productName) const
{
return strcmp(allowedProductName, productName) == 0;
}
bool ProductRack::isEmpty() const
{
return productCount == 0;
}
bool ProductRack::isFull() const
{
return productCount == MAX_PRODUCTS;
}
bool ProductRack::addProduct(Product* pProduct)
{
if ( isFull() )
{
statusPanel.displayMessage(StatusPanel::MESSAGECODE_RACK_IS_FULL);
return false;
}
else if ( not isCompatibleProduct(pProduct->getName()) )
{
statusPanel.displayMessage(StatusPanel::MESSAGECODE_PRODUCT_DOES_NOT_MATCH_PRODUCT_RACK);
return false;
}
else
{
products[productCount++] = pProduct;
return true;
}
}
bool ProductRack::deliverProduct()
{
if ( isEmpty() )
{
statusPanel.displayMessage(StatusPanel::MESSAGECODE_SOLD_OUT);
return false;
}
else if ( deliveryChute.insertProduct(products[productCount - 1]) )
{
--productCount;
return true;
}
else
{
return false;
}
}
unsigned ProductRack::getNumProductsInRack() const
{
return productCount;
}
unsigned ProductRack::getProductPriceCents() const
{
return productPriceCents;
}
/*
* Vending Machine Project
* CSE-40477
*
* ProductRack.h
* YOU MUST NOT CHANGE THIS FILE.
*/
#ifndef PROJECT1_PRODUCTRACK_H
#define PROJECT1_PRODUCTRACK_H
#include "DeliveryChute.h"
#include "Product.h"
#include "StatusPanel.h"
namespace Project1
{
//=========================================================================
// SUMMARY
// A rack to hold a pre-determined number of products all of the same
// type. The rack has access to a delivery chute into which products
// may be dropped.
//
// RESOURCES
// A product rack owns (i.e. is responsible for destroying)
// all products that are inserted into the rack. The product rack
// relinquishes ownership of all products that are removed from the
// rack.
//=========================================================================
class ProductRack
{
public:
static const int MAX_PRODUCTS = 5;
//---------------------------------------------------------------------
// SUMMARY
// Constructor. Creates a product rack to hold products with the
// allowed product name. The product rack will deliver products
// into the given delivery chute. The price of each product
// in the rack is given by the productPriceCents parameter.
//
// RESOURCES
// The product rack is initialized to not own any products.
//
// PARAMETERS
// statusPanel
// The status panel to which messages will be output.
// allowedProductName
// Products added to this rack must have this name.
// deliveryChute
// The delivery chute to which products will be delivered by
// this rack.
// productPriceCents
// The price of each product contained within this rack.
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
ProductRack(
StatusPanel &statusPanel,
const char *allowedProductName,
DeliveryChute &deliveryChute,
unsigned productPriceCents);
//---------------------------------------------------------------------
// SUMMARY
// Destructor. Destroys the product rack.
//
// RESOURCES
// All owned products are freed.
//
// PARAMETERS
// None
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
~ProductRack();
//---------------------------------------------------------------------
// SUMMARY
// Returns whether the given product name matches the name of
// products allowed to be stored in this rack.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// Whether the given product name matches the name of products
// allowed to be stored in this rack.
//---------------------------------------------------------------------
bool isCompatibleProduct(const char *productName) const;
//---------------------------------------------------------------------
// SUMMARY
// Returns whether this rack contains zero products.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// Whether this rack contains zero products.
//---------------------------------------------------------------------
bool isEmpty() const;
//---------------------------------------------------------------------
// SUMMARY
// Return whethers this rack contains a number of products equal to
// MAX_PRODUCTS.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// Whether this rack contains a number of products equal to
// MAX_PRODUCTS.
//---------------------------------------------------------------------
bool isFull() const;
//---------------------------------------------------------------------
// SUMMARY
// Adds the given product to the rack if the rack is not full and
// the product name matches the allowed product name of this rack.
// If the rack is full MESSAGECODE_RACK_IS_FULL will be output to
// the status panel. If the product is not compatible with the
// rack MESSAGECODE_PRODUCT_DOES_NOT_MATCH_PRODUCT_RACK will be
// output to the status panel.
//
// RESOURCES
// The rack takes ownership of the product if and only the product
// was successfully added to the rack.
//
// PARAMETERS
// pProduct
// The product to be added to the rack.
//
// RETURNS
// Whether the product was added to the rack.
//---------------------------------------------------------------------
bool addProduct(Product* pProduct);
//---------------------------------------------------------------------
// SUMMARY
// Inert the next product from the rack into the delivery chute if
// the delivery chute is empty and the rack is not empty. If the
// rack is empty MESSAGECODE_SOLD_OUT will be output to the status
// panel.
//
// RESOURCES
// The rack relinquishes ownserhip of the product if and only if
// the next product was successfully dropped into the delivery
// chute.
//
// PARAMETERS
// None
//
// RETURNS
// Whether the next product was successfully dropped into the
// delivery chute.
//---------------------------------------------------------------------
bool deliverProduct();
//---------------------------------------------------------------------
// SUMMARY
// Return the number of products in the rack.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// The number of products in the rack.
//---------------------------------------------------------------------
unsigned getNumProductsInRack() const;
//---------------------------------------------------------------------
// SUMMARY
// Return the price (in cents) of the products contained by this
// rack.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// The price (in cents) of the products contained by this rack.
//---------------------------------------------------------------------
unsigned getProductPriceCents() const;
private:
StatusPanel &statusPanel;
char allowedProductName[Product::MAX_NAME_LENGTH];
DeliveryChute &deliveryChute;
Product *products[MAX_PRODUCTS];
unsigned productCount;
unsigned productPriceCents;
};
}
#endif
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
/*
* Vending Machine Project
* CSE-40477
*
* StatusPanel.cpp
* YOU MUST NOT CHANGE THIS FILE.
*/
#include "StatusPanel.h"
const char *Project1::StatusPanel::messages[MESSAGECODE_ERRORS_END] =
{
"No rack designated for the named product\n",
"The rack for the named product is full\n",
"Product name does not match product rack allowed name\n",
"Bad coin inserted\n",
"Product sold out\n",
"Cannot purchase product; a product is already in delivery chute\n",
"You're pressing a nasty old chewing gum wad, not a button!\n",
"Insufficient money to make purchase, insert more money then try again\n"
};
Project1::StatusPanel::StatusPanel(ostream &outputStream)
: outputStream(outputStream)
{
}
void
Project1::StatusPanel::displayMessage(MessageCode messageCode)
{
outputStream << StatusPanel::messages[messageCode];
}
/*
* Vending Machine Project
* CSE-40477
*
* StatusPanel.h
* YOU MUST NOT CHANGE THIS FILE.
*/
#ifndef PROJECT1_STATUSPANEL_H
#define PROJECT1_STATUSPANEL_H
#include <ostream>
using std::ostream;
namespace Project1
{
//=========================================================================
// SUMMARY
// A status panel that will display messages. A status panel is
// associated with an ostream (e.g. cout); the messages displayed by
// the status panel are sent to this ostream.
//
// RESOURCES
// A status panel does not own any resources.
//=========================================================================
class StatusPanel
{
public:
//---------------------------------------------------------------------
// Codes for messages that can be displayed by a status panel. Each
// enumerator's value is an index into an array of associated error
// messages.
//---------------------------------------------------------------------
enum MessageCode
{
// There is no product rack for the given product
MESSAGECODE_NO_RACK_FOR_PRODUCT,
// The rack to which you are attempting to add a product is full
MESSAGECODE_RACK_IS_FULL,
// Attempt to insert product into a rack that expects a different
// product name
MESSAGECODE_PRODUCT_DOES_NOT_MATCH_PRODUCT_RACK,
// The coin you've attempted to insert is not accepted by the
// vending machine
MESSAGECODE_BAD_COIN,
// The product you've attempted to purchase is sold out
MESSAGECODE_SOLD_OUT,
// You tried to purchase a product but the delivery chute is full
MESSAGECODE_CHUTE_FULL,
// A button corresponding to the name you've provided does not
// exist
MESSAGECODE_WRONG_BUTTON,
// You pressed a button but haven't inserted enough money for the
// purchase
MESSAGECODE_INSUFFICIENT_MONEY,
// End of error code range (not a real message); used to size
// array messages
MESSAGECODE_ERRORS_END
};
//---------------------------------------------------------------------
// SUMMARY
// Constructor. Initializes the status panel's stream to the
// given ostream. Messages output by the status panel will be
// sent to the ostream.
//
// RESOURCES
// None
//
// PARAMETERS
// outputStream
// The ostream to which messages will be sent.
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
StatusPanel(ostream &outputStream);
//---------------------------------------------------------------------
// SUMMARY
// Sends the message string for the given message code to this
// status panel's output stream.
//
// RESOURCES
// None
//
// PARAMETERS
// messageCode
// The message code of the message that should be output to
// the status panel's output stream.
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
void displayMessage(MessageCode messageCode);
private:
// Messages that can be displayed on the status panel. This array
// should be indexed using a MessageCode.
static const char *messages[MESSAGECODE_ERRORS_END];
ostream &outputStream;
};
}
#endif
/*
* Vending Machine Project
* CSE-40477
*
* TestFramework.cpp
* YOU MUST NOT CHANGE THIS FILE.
*/
#include <iostream>
using std::endl;
#include <sstream>
using std::stringstream;
#include <string>
using std::string;
#include "TestFramework.h"
// Initialize static members
ostream *TestFramework::pos = 0;
string TestFramework::name = "";
vector<string> TestFramework::assertionResults = vector<string>();
bool TestFramework::passed = true;
unsigned TestFramework::totalTestsPassed = 0;
unsigned TestFramework::totalTestsFailed = 0;
void
TestFramework::Initialize(ostream *pos)
{
TestFramework::pos = pos;
}
void
TestFramework::BeginTest(string name)
{
*TestFramework::pos << ">>>>>> Beginning test '" << name << "'" << endl;
TestFramework::name = name;
TestFramework::assertionResults.clear();
TestFramework::passed = true;
}
bool
TestFramework::Assert(string expression, bool condition, string file, unsigned line)
{
if (!condition)
{
stringstream ss;
TestFramework::passed = false;
ss << file << ":" << line << ":0: Assertion failed:\t" << expression;
TestFramework::assertionResults.push_back(ss.str());
}
return condition;
}
void
TestFramework::EndTest()
{
if (TestFramework::passed)
{
++TestFramework::totalTestsPassed;
}
else
{
++TestFramework::totalTestsFailed;
}
// Display assertionResults
for (size_t i = 0; i < TestFramework::assertionResults.size(); ++i)
{
*TestFramework::pos << TestFramework::assertionResults[i]
<< endl;
}
// Display test result
*TestFramework::pos << "<<<<<< Ending test '" << TestFramework::name
<< "' ";
*TestFramework::pos << "---> "
<< (TestFramework::passed ? "Passed" : "FAILED") << endl << endl;
}
void
TestFramework::DisplaySummary()
{
*TestFramework::pos << TestFramework::totalTestsPassed
<< " tests passed, " << TestFramework::totalTestsFailed
<< " tests failed" << endl << endl;
}
/*
* Vending Machine Project
* CSE-40477
*
* TestFramework.h
* YOU MUST NOT CHANGE THIS FILE.
*/
#ifndef TESTFRAMEWORK_H
#define TESTFRAMEWORK_H
#include <iostream>
using std::ostream;
#include <string>
using std::string;
#include <vector>
using std::vector;
#define TEST_ASSERT(expression) \
(TestFramework::Assert(#expression, (expression), __FILE__, __LINE__))
class TestFramework
{
public:
static void Initialize(ostream *pos);
static void BeginTest(string name);
static bool Assert(string expression, bool result, string file, unsigned line);
static void EndTest();
static void DisplaySummary();
private:
TestFramework();
static ostream *pos;
static string name;
static vector<string> assertionResults;
static bool passed;
static unsigned totalTestsPassed;
static unsigned totalTestsFailed;
};
#endif
/*
* Vending Machine Project
* CSE-40477
*
* UnitTest.cpp
* YOU MUST NOT CHANGE THIS FILE.
*/
#include <cstring>
using std::strcmp;
#include <fstream>
using std::ostream;
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
#include <sstream>
using std::stringstream;
#include "TestFramework.h"
#include "UnitTest.h"
#include "VendingMachine.h"
using Project1::Coin;
using Project1::DeliveryChute;
using Project1::Product;
using Project1::ProductButton;
using Project1::ProductRack;
using Project1::StatusPanel;
using Project1::VendingMachine;
const char *productNames[] =
{
"Coke",
"Pepsi",
"Sprite"
};
unsigned productPrices[] =
{
25,
25,
25
};
void
Project1UnitTest::Initialize(ostream &out)
{
TestFramework::Initialize(&out);
}
void
Project1UnitTest::DisplaySummary()
{
TestFramework::DisplaySummary();
}
void
TestCoin_CoinTypeConstructor()
{
TestFramework::BeginTest("TestCoin_CoinTypeConstructor");
// CoinType constructor
Coin coinCoinType(Coin::COINTYPE_PENNY);
TEST_ASSERT(Coin::COINTYPE_PENNY == coinCoinType.getDenomination());
TestFramework::EndTest();
}
void
TestCoin_AllDenominationsAndValues()
{
TestFramework::BeginTest("TestCoin_AllDenominationsAndValues");
// All denominations and values
Coin coin1(Coin::COINTYPE_PENNY);
TEST_ASSERT(Coin::COINTYPE_PENNY == coin1.getDenomination());
TEST_ASSERT(1 == coin1.getValueCents());
Coin coin2(Coin::COINTYPE_NICKEL);
TEST_ASSERT(Coin::COINTYPE_NICKEL == coin2.getDenomination());
TEST_ASSERT(5 == coin2.getValueCents());
Coin coin3(Coin::COINTYPE_DIME);
TEST_ASSERT(Coin::COINTYPE_DIME == coin3.getDenomination());
TEST_ASSERT(10 == coin3.getValueCents());
Coin coin4(Coin::COINTYPE_QUARTER);
TEST_ASSERT(Coin::COINTYPE_QUARTER == coin4.getDenomination());
TEST_ASSERT(25 == coin4.getValueCents());
Coin coin5(Coin::COINTYPE_HALF_DOLLAR);
TEST_ASSERT(Coin::COINTYPE_HALF_DOLLAR == coin5.getDenomination());
TEST_ASSERT(50 == coin5.getValueCents());
Coin coin6(Coin::COINTYPE_DOLLAR);
TEST_ASSERT(Coin::COINTYPE_DOLLAR == coin6.getDenomination());
TEST_ASSERT(100 == coin6.getValueCents());
TestFramework::EndTest();
}
void
Project1UnitTest::TestCoin()
{
TestCoin_CoinTypeConstructor();
TestCoin_AllDenominationsAndValues();
}
void
TestProduct_Constructor()
{
TestFramework::BeginTest("TestProduct_Constructor");
// Constructor
Product p1("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(strcmp("Coca Cola", p1.getBrand()) == 0);
TEST_ASSERT(strcmp("Coke", p1.getName()) == 0);
TEST_ASSERT(strcmp("12 fl oz", p1.getSize()) == 0);
TestFramework::EndTest();
}
void
TestProduct_GettersSetters()
{
TestFramework::BeginTest("TestProduct_GettersSetters");
// All getters & setters
Product p2("", "", "");
p2.setBrand("Pepsi Cola");
TEST_ASSERT(strcmp("Pepsi Cola", p2.getBrand()) == 0);
p2.setName("Pepsi");
TEST_ASSERT(strcmp("Pepsi", p2.getName()) == 0);
p2.setSize("24 fl oz");
TEST_ASSERT(strcmp("24 fl oz", p2.getSize()) == 0);
TestFramework::EndTest();
}
void
Project1UnitTest::TestProduct()
{
TestProduct_Constructor();
TestProduct_GettersSetters();
}
void
TestDeliveryChute_Constructor()
{
TestFramework::BeginTest("TestDeliveryChute_Constructor");
// Constructor
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute d1(sp1);
TEST_ASSERT(0 == d1.retrieveProduct());
TestFramework::EndTest();
}
void
TestDeliveryChute_DroppingProductIntoEmptyChute()
{
TestFramework::BeginTest(
"TestDeliveryChute_DroppingProductIntoEmptyChute");
// Dropping product into empty chute
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute d2(sp1);
TEST_ASSERT(d2.insertProduct(
new Product("Coca Cola", "Coke", "12 fl oz")));
Product *p2 = d2.retrieveProduct();
TEST_ASSERT(0 != p2);
TEST_ASSERT(strcmp("Coca Cola", p2->getBrand()) == 0);
delete p2;
TestFramework::EndTest();
}
void
TestDeliveryChute_DroppingProductIntoNonEmptyChute()
{
TestFramework::BeginTest(
"TestDeliveryChute_DroppingProductIntoNonEmptyChute");
// Dropping product into non-empty chute
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute d3(sp1);
d3.insertProduct(new Product("Coca Cola", "Coke", "12 fl oz"));
Product *p3 = new Product("Pepsi Cola", "Pepsi", "24 fl oz");
TEST_ASSERT(!d3.insertProduct(p3));
delete p3;
p3 = d3.retrieveProduct();
TEST_ASSERT(p3 != 0);
TEST_ASSERT(strcmp("Coca Cola", p3->getBrand()) == 0);
delete p3;
TestFramework::EndTest();
}
void
TestDeliveryChute_ErrorMessages()
{
TestFramework::BeginTest(
"TestDeliveryChute_ErrorMessages");
// Insert product into full chute
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute dc1(sp1);
dc1.insertProduct(new Product("Coca Cola", "Coke", "12 fl oz"));
Product *p1 = new Product("Pepsi Cola", "Pepsi", "24 fl oz");
TEST_ASSERT(!dc1.insertProduct(p1));
TEST_ASSERT("Cannot purchase product; a product is already in delivery chute\n" == ss1.str());
delete p1;
TestFramework::EndTest();
}
void
Project1UnitTest::TestDeliveryChute()
{
TestDeliveryChute_Constructor();
TestDeliveryChute_DroppingProductIntoEmptyChute();
TestDeliveryChute_DroppingProductIntoNonEmptyChute();
TestDeliveryChute_ErrorMessages();
}
void
TestProductRack_Constructor()
{
TestFramework::BeginTest("TestProductRack_Constructor");
// Constructor
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c1(sp1);
ProductRack pr1(sp1, "Coke", c1, 25);
TEST_ASSERT(pr1.isCompatibleProduct("Coke"));
TEST_ASSERT(!pr1.isCompatibleProduct("Pepsi"));
TestFramework::EndTest();
}
void
TestProductRack_deliverProductFromEmptyRack()
{
TestFramework::BeginTest("TestProductRack_deliverProductFromEmptyRack");
// Deliver product from empty rack
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c2(sp1);
ProductRack pr2(sp1, "Coke", c2, 25);
TEST_ASSERT(!pr2.deliverProduct());
TestFramework::EndTest();
}
void
TestProductRack_AddAndDeliverOneProduct()
{
TestFramework::BeginTest("TestProductRack_AddAndDeliverOneProduct");
// Add & deliver 1 product
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c3(sp1);
ProductRack pr3(sp1, "Coke", c3, 25);
Product *p3 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(pr3.addProduct(p3));
TEST_ASSERT(pr3.deliverProduct());
Product *p3Copy = c3.retrieveProduct();
TEST_ASSERT(p3 == p3Copy);
delete p3Copy;
bool ret = pr3.deliverProduct();
TEST_ASSERT(!ret);
TestFramework::EndTest();
}
void
TestProductRack_AddAndDeliverTwoProducts()
{
TestFramework::BeginTest("TestProductRack_AddAndDeliverTwoProducts");
// Add & deliver 2 products
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c4(sp1);
ProductRack pr4(sp1, "Coke", c4, 25);
TEST_ASSERT(pr4.addProduct(new Product("Coca Cola", "Coke", "12 fl oz")));
TEST_ASSERT(pr4.addProduct(new Product("Coca Cola", "Coke", "12 fl oz")));
Product *p4;
TEST_ASSERT(pr4.deliverProduct());
p4 = c4.retrieveProduct();
TEST_ASSERT(0 != p4);
TEST_ASSERT(strcmp("Coke", p4->getName()) == 0);
delete p4;
TEST_ASSERT(pr4.deliverProduct());
p4 = c4.retrieveProduct();
TEST_ASSERT(0 != p4);
TEST_ASSERT(strcmp("Coke", p4->getName()) == 0);
delete p4;
bool ret = pr4.deliverProduct();
TEST_ASSERT(!ret);
TestFramework::EndTest();
}
void
TestProductRack_AddTooManyProducts()
{
TestFramework::BeginTest("TestProductRack_AddTooManyProducts");
// Add too many products
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c6(sp1);
ProductRack pr6(sp1, "Coke", c6, 25);
for (int i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
TEST_ASSERT(pr6.addProduct(
new Product("Coca Cola", "Coke", "12 fl oz")));
}
Product *p6 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(!pr6.addProduct(p6));
delete p6;
for (int i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
TEST_ASSERT(pr6.deliverProduct());
p6 = c6.retrieveProduct();
TEST_ASSERT(0 != p6);
delete p6;
}
TEST_ASSERT(!pr6.deliverProduct());
TestFramework::EndTest();
}
void
TestProductRack_InvalidProductName()
{
TestFramework::BeginTest("TestProductRack_InvalidProductName");
// Add product name that doesn't match an allowed name in a product rack
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c6(sp1);
ProductRack pr6(sp1, "Coke", c6, 25);
Product *p6 = new Product(
"Coca Cola", "Unmatched product name", "12 fl oz");
TEST_ASSERT(!pr6.addProduct(p6));
delete p6;
TestFramework::EndTest();
}
void
TestProductRack_IsEmpty()
{
TestFramework::BeginTest("TestProductRack_IsEmpty");
// Verify empty
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c6(sp1);
ProductRack pr6(sp1, "Coke", c6, 25);
TEST_ASSERT(pr6.isEmpty());
// Verify not empty
Product *p6 = new Product("Coca Cola", "Coke", "12 fl oz");
pr6.addProduct(p6);
TEST_ASSERT(!pr6.isEmpty());
TEST_ASSERT(pr6.deliverProduct());
TEST_ASSERT(0 != c6.retrieveProduct());
delete p6;
TestFramework::EndTest();
}
void
TestProductRack_IsFull()
{
TestFramework::BeginTest("TestProductRack_IsFull");
// Verify not full
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c6(sp1);
ProductRack pr6(sp1, "Coke", c6, 25);
TEST_ASSERT(!pr6.isFull());
// Verify full
Product *p6;
for (int i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
p6 = new Product("Coca Cola", "Coke", "12 fl oz");
pr6.addProduct(p6);
}
TEST_ASSERT(pr6.isFull());
for (int i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
TEST_ASSERT(pr6.deliverProduct());
TEST_ASSERT((p6 = c6.retrieveProduct()) != 0);
delete p6;
}
TestFramework::EndTest();
}
void
TestProductRack_ErrorMessages()
{
TestFramework::BeginTest(
"TestProductRack_ErrorMessages");
// Insert product into full rack
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute dc1(sp1);
ProductRack pr1(sp1, "Coke", dc1, 25);
Product *p1;
for (int i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
p1 = new Product("Coca Cola", "Coke", "12 fl oz");
pr1.addProduct(p1);
}
p1 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(!pr1.addProduct(p1));
delete p1;
TEST_ASSERT("The rack for the named product is full\n" == ss1.str());
// Insert incompatible product into rack
stringstream ss2;
StatusPanel sp2(ss2);
DeliveryChute dc2(sp2);
ProductRack pr2(sp2, "Coke", dc2, 25);
Product *p2 = new Product("Pepsi Cola", "Pepsi", "24 fl oz");
TEST_ASSERT(!pr2.addProduct(p2));
delete p2;
TEST_ASSERT("Product name does not match product rack allowed name\n" == ss2.str());
// Deliver product from empty rack
stringstream ss3;
StatusPanel sp3(ss3);
DeliveryChute dc3(sp3);
ProductRack pr3(sp3, "Coke", dc3, 25);
TEST_ASSERT(!pr3.deliverProduct());
TEST_ASSERT("Product sold out\n" == ss3.str());
TestFramework::EndTest();
}
void
Project1UnitTest::TestProductRack()
{
TestProductRack_Constructor();
TestProductRack_deliverProductFromEmptyRack();
TestProductRack_AddAndDeliverOneProduct();
TestProductRack_AddAndDeliverTwoProducts();
TestProductRack_AddTooManyProducts();
TestProductRack_InvalidProductName();
TestProductRack_IsEmpty();
TestProductRack_IsFull();
TestProductRack_ErrorMessages();
}
void
TestProductButton_Constructor()
{
TestFramework::BeginTest("TestProductButton_Constructor");
// Constructor
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c1(sp1);
ProductRack pr1(sp1, "Coke", c1, 25);
ProductButton pb1(pr1);
TEST_ASSERT(!pb1.press());
TestFramework::EndTest();
}
void
TestProductButton_pressingWithAProductInTheRack()
{
TestFramework::BeginTest(
"TestProductButton_pressingWithAProductInTheRack");
// pressing with a product in the rack
stringstream ss1;
StatusPanel sp1(ss1);
DeliveryChute c2(sp1);
ProductRack pr2(sp1, "Coke", c2, 25);
ProductButton pb2(pr2);
Product *p2 = new Product("Coca Cola", "Coke", "12 fl oz");
pr2.addProduct(p2);
TEST_ASSERT(pb2.press());
Product *p2Copy = c2.retrieveProduct();
TEST_ASSERT(p2 == p2Copy);
delete p2Copy;
TestFramework::EndTest();
}
void
Project1UnitTest::TestProductButton()
{
TestProductButton_Constructor();
TestProductButton_pressingWithAProductInTheRack();
}
void
TestStatusPanel_DisplayMessage()
{
TestFramework::BeginTest("TestStatusPanel_Display");
// NO_RACK_FOR_PRODUCT
stringstream ss1;
StatusPanel sp1(ss1);
sp1.displayMessage(StatusPanel::MESSAGECODE_NO_RACK_FOR_PRODUCT);
TEST_ASSERT("No rack designated for the named product\n" == ss1.str());
// RACK_IS_FULL
stringstream ss2;
StatusPanel sp2(ss2);
sp2.displayMessage(StatusPanel::MESSAGECODE_RACK_IS_FULL);
TEST_ASSERT("The rack for the named product is full\n" == ss2.str());
// BAD_COIN
stringstream ss3;
StatusPanel sp3(ss3);
sp3.displayMessage(StatusPanel::MESSAGECODE_BAD_COIN);
TEST_ASSERT("Bad coin inserted\n" == ss3.str());
// SOLD_OUT
stringstream ss4;
StatusPanel sp4(ss4);
sp4.displayMessage(StatusPanel::MESSAGECODE_SOLD_OUT);
TEST_ASSERT("Product sold out\n" == ss4.str());
// WRONG_BUTTON
stringstream ss5;
StatusPanel sp5(ss5);
sp5.displayMessage(StatusPanel::MESSAGECODE_WRONG_BUTTON);
TEST_ASSERT("You're pressing a nasty old chewing gum wad, not a button!\n"
== ss5.str());
// INSUFFICIENT_MONEY
stringstream ss6;
StatusPanel sp6(ss6);
sp6.displayMessage(StatusPanel::MESSAGECODE_INSUFFICIENT_MONEY);
TEST_ASSERT(
"Insufficient money to make purchase, "
"insert more money then try again\n" == ss6.str());
TestFramework::EndTest();
}
void
Project1UnitTest::TestStatusPanel()
{
TestStatusPanel_DisplayMessage();
}
void
TestVendingMachine_Constructor()
{
TestFramework::BeginTest("TestVendingMachine_Constructor");
// Constructor
stringstream ss1;
VendingMachine vm1(ss1, productNames, productPrices);
TEST_ASSERT(0 == vm1.retrieveProduct());
TEST_ASSERT(0 == vm1.getProductCount("Coke"));
TEST_ASSERT(0 == vm1.getProductCount("Pepsi"));
TEST_ASSERT(0 == vm1.getProductCount("Sprite"));
TEST_ASSERT(0 == vm1.countTill());
TestFramework::EndTest();
}
void
TestVendingMachine_BuyOneProduct()
{
TestFramework::BeginTest("TestVendingMachine_BuyOneProduct");
// Buy 1 product
stringstream ss2;
VendingMachine vm2(ss2, productNames, productPrices);
Product *p2 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(vm2.addProduct(p2));
TEST_ASSERT(1 == vm2.getProductCount("Coke"));
TEST_ASSERT(0 == vm2.getProductCount("Pepsi"));
TEST_ASSERT(0 == vm2.getProductCount("Sprite"));
Coin *c2 = new Coin(Coin::COINTYPE_QUARTER);
TEST_ASSERT(vm2.insertCoin(c2));
TEST_ASSERT(vm2.pressButton(0));
Product *p2Copy = vm2.retrieveProduct();
TEST_ASSERT(p2 == p2Copy);
TEST_ASSERT(0 == vm2.getProductCount("Coke"));
TEST_ASSERT(25 == vm2.countTill());
delete p2Copy;
TestFramework::EndTest();
}
void
TestVendingMachine_BuyOneProductWithNoCoins()
{
TestFramework::BeginTest("TestVendingMachine_BuyOneProductWithNoCoins");
// Attempt to buy 1 product without inserting any coins
stringstream ss3;
VendingMachine vm3(ss3, productNames, productPrices);
Product *p3 = new Product("Coca Cola", "Coke", "12 fl oz");
vm3.addProduct(p3);
TEST_ASSERT(!vm3.pressButton(0));
Product *p3Copy = vm3.retrieveProduct();
TEST_ASSERT(0 == p3Copy);
TEST_ASSERT(1 == vm3.getProductCount("Coke"));
TEST_ASSERT(0 == vm3.countTill());
TestFramework::EndTest();
}
void
TestVendingMachine_BuyOneProductWithInsufficientCoins()
{
TestFramework::BeginTest(
"TestVendingMachine_BuyOneProductWithInsufficientCoins");
// Attempt to buy 1 product inserting insufficient coins
stringstream ss4;
VendingMachine vm4(ss4, productNames, productPrices);
Product *p4 = new Product("Coca Cola", "Coke", "12 fl oz");
vm4.addProduct(p4);
Coin *c4_1 = new Coin(Coin::COINTYPE_DIME);
Coin *c4_2 = new Coin(Coin::COINTYPE_PENNY);
vm4.insertCoin(c4_1);
vm4.insertCoin(c4_2);
TEST_ASSERT(!vm4.pressButton(0));
Product *p4Copy = vm4.retrieveProduct();
TEST_ASSERT(0 == p4Copy);
TEST_ASSERT(1 == vm4.getProductCount("Coke"));
TEST_ASSERT(11 == vm4.countTill());
TestFramework::EndTest();
}
void
TestVendingMachine_BuyOneProductWithTooManyCoins()
{
TestFramework::BeginTest(
"TestVendingMachine_BuyOneProductWithTooManyCoins");
// Buy 1 product entering more than enough money
stringstream ss5;
VendingMachine vm5(ss5, productNames, productPrices);
Product *p5 = new Product("Coca Cola", "Coke", "12 fl oz");
vm5.addProduct(p5);
Coin *c5 = new Coin(Coin::COINTYPE_DOLLAR);
vm5.insertCoin(c5);
TEST_ASSERT(vm5.pressButton(0));
Product *p5Copy = vm5.retrieveProduct();
TEST_ASSERT(p5 == p5Copy);
TEST_ASSERT(0 == vm5.getProductCount("Coke"));
TEST_ASSERT(100 == vm5.countTill());
delete p5Copy;
TestFramework::EndTest();
}
void
TestVendingMachine_insertCoinsForTwoProductsThenBuyTwo()
{
TestFramework::BeginTest(
"TestVendingMachine_insertCoinsForTwoProductsThenBuyTwo");
// Buy 2 products entering more than enough money before buying either
stringstream ss6;
VendingMachine vm6(ss6, productNames, productPrices);
Product *p6_1 = new Product("Coca Cola", "Coke", "12 fl oz");
Product *p6_2 = new Product("Pepsi Cola", "Pepsi", "12 fl oz");
vm6.addProduct(p6_1);
vm6.addProduct(p6_2);
Coin *c6 = new Coin(Coin::COINTYPE_DOLLAR);
vm6.insertCoin(c6);
TEST_ASSERT(vm6.pressButton(0));
Product *p6_1Copy = vm6.retrieveProduct();
TEST_ASSERT(p6_1 == p6_1Copy);
TEST_ASSERT(vm6.pressButton(1));
Product *p6_2Copy = vm6.retrieveProduct();
TEST_ASSERT(p6_2 == p6_2Copy);
TEST_ASSERT(0 == vm6.getProductCount("Coke"));
TEST_ASSERT(0 == vm6.getProductCount("Pepsi"));
TEST_ASSERT(100 == vm6.countTill());
delete p6_1Copy;
delete p6_2Copy;
TestFramework::EndTest();
}
void
TestVendingMachine_InsertAllValidCoins()
{
TestFramework::BeginTest("TestVendingMachine_InsertAllValidCoins");
// Insert every possible coin
stringstream ss7;
VendingMachine vm7(ss7, productNames, productPrices);
Coin *c7_1 = new Coin(Coin::COINTYPE_DIME);
Coin *c7_2 = new Coin(Coin::COINTYPE_DOLLAR);
Coin *c7_3 = new Coin(Coin::COINTYPE_HALF_DOLLAR);
Coin *c7_4 = new Coin(Coin::COINTYPE_NICKEL);
Coin *c7_5 = new Coin(Coin::COINTYPE_PENNY);
Coin *c7_6 = new Coin(Coin::COINTYPE_QUARTER);
TEST_ASSERT(vm7.insertCoin(c7_1));
TEST_ASSERT(vm7.insertCoin(c7_2));
TEST_ASSERT(vm7.insertCoin(c7_3));
TEST_ASSERT(vm7.insertCoin(c7_4));
TEST_ASSERT(vm7.insertCoin(c7_5));
TEST_ASSERT(vm7.insertCoin(c7_6));
TEST_ASSERT(191 == vm7.countTill());
TestFramework::EndTest();
}
void
TestVendingMachine_InsertInvalidCoin()
{
TestFramework::BeginTest("TestVendingMachine_InsertInvalidCoin");
// Insert invalid coin
stringstream ss8;
VendingMachine vm8(ss8, productNames, productPrices);
Coin *c8 = new Coin(Coin::COINTYPE_WOODEN_NICKEL);
TEST_ASSERT(!vm8.insertCoin(c8));
TEST_ASSERT(0 == vm8.countTill());
delete c8;
TestFramework::EndTest();
}
void
TestVendingMachine_InsertInvalidProduct()
{
TestFramework::BeginTest("TestVendingMachine_InsertInvalidProduct");
// Add an invalid product
stringstream ss9;
VendingMachine vm9(ss9, productNames, productPrices);
Product *p9 = new Product("7Up, The Un-Cola", "7Up", "12 fl oz");
TEST_ASSERT(!vm9.addProduct(p9));
TEST_ASSERT(0 == vm9.getProductCount("Coke"));
TEST_ASSERT(0 == vm9.getProductCount("Pepsi"));
TEST_ASSERT(0 == vm9.getProductCount("Sprite"));
delete p9;
TestFramework::EndTest();
}
void
TestVendingMachine_AddTooManyProducts()
{
TestFramework::BeginTest("TestVendingMachine_AddTooManyProducts");
// Add too many products
stringstream ss10;
VendingMachine vm10(ss10, productNames, productPrices);
Product *p10;
for (int i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
p10 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(vm10.addProduct(p10));
}
p10 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(!vm10.addProduct(p10));
delete p10;
TestFramework::EndTest();
}
void
TestVendingMachine_BuyAndSellFullCapacityOfRacks()
{
TestFramework::BeginTest(
"TestVendingMachine_BuyAndSellFullCapacityOfRacks");
// Buy / sell full capacity of each rack
stringstream ss11;
VendingMachine vm11(ss11, productNames, productPrices);
int i;
for (i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
TEST_ASSERT(vm11.addProduct(
new Product("Coca Cola", "Coke", "12 fl oz")));
TEST_ASSERT(vm11.addProduct(
new Product("Pepsi Cola", "Pepsi", "12 fl oz")));
TEST_ASSERT(vm11.addProduct(
new Product("Sprite", "Sprite", "12 fl oz")));
}
TEST_ASSERT(ProductRack::MAX_PRODUCTS == vm11.getProductCount("Coke"));
TEST_ASSERT(ProductRack::MAX_PRODUCTS == vm11.getProductCount("Pepsi"));
TEST_ASSERT(ProductRack::MAX_PRODUCTS == vm11.getProductCount("Sprite"));
for (i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
Coin *c11 = new Coin(Coin::COINTYPE_QUARTER);
TEST_ASSERT(vm11.insertCoin(c11));
TEST_ASSERT(vm11.pressButton(0));
Product *p11 = vm11.retrieveProduct();
TEST_ASSERT(0 != p11);
delete p11;
}
for (i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
Coin *c11 = new Coin(Coin::COINTYPE_QUARTER);
TEST_ASSERT(vm11.insertCoin(c11));
TEST_ASSERT(vm11.pressButton(1));
Product *p11 = vm11.retrieveProduct();
TEST_ASSERT(0 != p11);
delete p11;
}
for (i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
Coin *c11 = new Coin(Coin::COINTYPE_QUARTER);
TEST_ASSERT(vm11.insertCoin(c11));
TEST_ASSERT(vm11.pressButton(2));
Product *p11 = vm11.retrieveProduct();
TEST_ASSERT(0 != p11);
delete p11;
}
TEST_ASSERT(0 == vm11.getProductCount("Coke"));
TEST_ASSERT(0 == vm11.getProductCount("Pepsi"));
TEST_ASSERT(0 == vm11.getProductCount("Sprite"));
TEST_ASSERT(3 * ProductRack::MAX_PRODUCTS * 25 == vm11.countTill());
TestFramework::EndTest();
}
void
TestVendingMachine_ErrorMessages()
{
TestFramework::BeginTest("TestVendingMachine_VerifyErrorMessages");
// NO_RACK_FOR_PRODUCT error message
stringstream ss13;
VendingMachine vm13(ss13, productNames, productPrices);
Product *p13 = new Product("7Up, The Un-Cola", "7Up", "12 fl oz");
TEST_ASSERT(!vm13.addProduct(p13));
delete p13;
TEST_ASSERT("No rack designated for the named product\n" == ss13.str());
// RACK_IS_FULL error message (should be handled by internal rack)
stringstream ss14;
VendingMachine vm14(ss14, productNames, productPrices);
Product *p14;
for (int i = 0; i < ProductRack::MAX_PRODUCTS; ++i)
{
p14 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(vm14.addProduct(p14));
}
p14 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(!vm14.addProduct(p14));
delete p14;
TEST_ASSERT("The rack for the named product is full\n" == ss14.str());
// BAD_COIN error message
stringstream ss15;
VendingMachine vm15(ss15, productNames, productPrices);
Coin *c15 = new Coin(Coin::COINTYPE_WOODEN_NICKEL);
TEST_ASSERT(!vm15.insertCoin(c15));
TEST_ASSERT("Bad coin inserted\n" == ss15.str());
delete c15;
// SOLD_OUT error message )(should be handled by internal rack)
stringstream ss16;
VendingMachine vm16(ss16, productNames, productPrices);
Coin *c16 = new Coin(Coin::COINTYPE_QUARTER);
TEST_ASSERT(vm16.insertCoin(c16));
TEST_ASSERT(!vm16.pressButton(0));
TEST_ASSERT("Product sold out\n" == ss16.str());
// WRONG_BUTTON error message
stringstream ss17;
VendingMachine vm17(ss17, productNames, productPrices);
Coin *c17 = new Coin(Coin::COINTYPE_QUARTER);
TEST_ASSERT(vm17.insertCoin(c17));
TEST_ASSERT(!vm17.pressButton(7));
TEST_ASSERT(
"You're pressing a nasty old chewing gum wad, not a button!\n"
== ss17.str());
// INSUFFICIENT_MONEY error message
stringstream ss18;
VendingMachine vm18(ss18, productNames, productPrices);
Coin *c18 = new Coin(Coin::COINTYPE_PENNY);
TEST_ASSERT(vm18.insertCoin(c18));
Product *p18 = new Product("Coca Cola", "Coke", "12 fl oz");
TEST_ASSERT(vm18.addProduct(p18));
TEST_ASSERT(!vm18.pressButton(0));
TEST_ASSERT(
"Insufficient money to make purchase, "
"insert more money then try again\n" == ss18.str());
TestFramework::EndTest();
}
void
TestVendingMachine_CoinBox()
{
TestFramework::BeginTest("TestVendingMachine_CoinBox");
// Constructor
stringstream ss1;
VendingMachine vm1(ss1, productNames, productPrices);
vm1.insertCoin(new Coin(Coin::COINTYPE_PENNY));
vm1.insertCoin(new Coin(Coin::COINTYPE_NICKEL));
vm1.insertCoin(new Coin(Coin::COINTYPE_DIME));
vm1.insertCoin(new Coin(Coin::COINTYPE_QUARTER));
vm1.insertCoin(new Coin(Coin::COINTYPE_HALF_DOLLAR));
vm1.insertCoin(new Coin(Coin::COINTYPE_DOLLAR));
TEST_ASSERT(191 == vm1.countTill());
unsigned numCoins = vm1.getNumCoinsInCoinBox();
TEST_ASSERT(6 == numCoins);
TestFramework::EndTest();
}
void
Project1UnitTest::TestVendingMachine()
{
TestVendingMachine_Constructor();
TestVendingMachine_BuyOneProduct();
TestVendingMachine_BuyOneProductWithNoCoins();
TestVendingMachine_BuyOneProductWithInsufficientCoins();
TestVendingMachine_BuyOneProductWithTooManyCoins();
TestVendingMachine_insertCoinsForTwoProductsThenBuyTwo();
TestVendingMachine_InsertAllValidCoins();
TestVendingMachine_InsertInvalidProduct();
TestVendingMachine_AddTooManyProducts();
TestVendingMachine_BuyAndSellFullCapacityOfRacks();
TestVendingMachine_ErrorMessages();
TestVendingMachine_CoinBox();
}
/*
* Vending Machine Project
* CSE-40477
*
* UnitTest.h
* YOU MUST NOT CHANGE THIS FILE.
*
* Note: In real-world development, a software developer writes unit tests
* for the code s/he produces. These unit tests are intended to fully test
* the public interface of each class. When an internal change is made
* to a class, the developer can re-run the unit-tests to verify that
* no part of the public interface was broken by the change. Writing unit
* tests serves three major purposes:
* 1. Unit tests allow you to test the functionality of each class
* incrementally as development proceeds.
* 2. Unit tests allow you to make changes to the internal implementation
* of a class and be confident that the change hasn't broken
* the public interface.
* 3. Unit tests serve to document the public interface of each class.
* Since unit tests demonstrate what is expected of a class' public
* interface, they document how to use the class. Unlike comments, the
* self-documentation provided by unit tests won't become out-of-sync
* with the classes, since the unit tests are compiled against
* the classes.
*/
#ifndef UNITTEST_H
#define UNITTEST_H
#include <ostream>
using std::ostream;
namespace Project1UnitTest
{
void Initialize(ostream &out);
void DisplaySummary();
void TestNothing();
void TestCoin();
void TestProduct();
void TestDeliveryChute();
void TestProductRack();
void TestProductButton();
void TestStatusPanel();
void TestVendingMachine();
}
#endif
// -----------------------------------------------------------------------------
// utils.h
// -----------------------------------------------------------------------------
#pragma once
// Windows...
#ifdef WIN32
# define or ||
# define and &&
# define not !
#endif
#define warn std::cerr \
<< "warning: In function ‘" << __FUNCTION__ << "’:" << std::endl \
<< "\t"
/*
* Vending Machine Project
* CSE-40477
*
* VendingMachine.cpp
* YOU MUST IMPLEMENT THE FUNCTIONS IN THIS FILE.
*/
#include "utils.h"
#include "VendingMachine.h"
using Project1::VendingMachine;
#include "Coin.h"
using Project1::Coin;
#include "Product.h"
using Project1::Product;
VendingMachine::VendingMachine(ostream &statusPanelStream,
const char *productNames[NUM_PRODUCT_RACKS],
unsigned productPrices[NUM_PRODUCT_RACKS])
: statusPanel(statusPanelStream),
deliveryChute(statusPanel),
numCoins(0),
unspentMoneyCents(0)
{
// Initialize the vending machine to contain one product button connected
// to a product rack for each entry in the productNames array.
for (size_t i = 0; i < NUM_PRODUCT_RACKS; ++i)
{
productRacks[i] = new ProductRack(statusPanel, productNames[i],
deliveryChute, productPrices[i]);
productButtons[i] = new ProductButton(*productRacks[i]);
}
}
VendingMachine::~VendingMachine()
{
for (size_t i = 0; i < NUM_PRODUCT_RACKS; ++i)
{
delete productRacks[i];
delete productButtons[i];
}
for (size_t i = 0; i < numCoins; ++i)
{
delete coinBox[i];
}
}
bool VendingMachine::insertCoin(Coin *pCoin)
{
if (pCoin->getDenomination() == Coin::COINTYPE_WOODEN_NICKEL)
{
statusPanel.displayMessage(StatusPanel::MESSAGECODE_BAD_COIN);
return false;
}
else
{
coinBox[numCoins++] = pCoin;
unspentMoneyCents += pCoin->getValueCents();
return true;
}
}
bool VendingMachine::pressButton(int button)
{
if (button < 0 or button >= NUM_PRODUCT_RACKS)
{
statusPanel.displayMessage(StatusPanel::MESSAGECODE_WRONG_BUTTON);
return false;
}
ProductButton& productButton = *productButtons[button];
if (unspentMoneyCents < productButton.getProductPriceCents())
{
statusPanel.displayMessage(StatusPanel::MESSAGECODE_INSUFFICIENT_MONEY);
return false;
}
return productButton.press();
}
Product * VendingMachine::retrieveProduct()
{
return deliveryChute.retrieveProduct();
}
bool VendingMachine::addProduct(Product *pProduct)
{
for (size_t i = 0; i < NUM_PRODUCT_RACKS; ++i)
{
ProductRack& productRack = *productRacks[i];
if ( productRack.isCompatibleProduct(pProduct->getName()) )
{
// In case of multiple productRack for the same product
// Does not pass 'TestVendingMachine_VerifyErrorMessages'
// if ( productRack.addProduct(pProduct) )
// {
// return true;
// }
return productRack.addProduct(pProduct);
}
}
statusPanel.displayMessage(StatusPanel::MESSAGECODE_NO_RACK_FOR_PRODUCT);
return false;
}
unsigned VendingMachine::getProductCount(const char *productName) const
{
unsigned count = 0;
for (size_t i = 0; i < NUM_PRODUCT_RACKS; ++i)
{
ProductRack& productRack = *productRacks[i];
if (productRack.isCompatibleProduct(productName))
{
count += productRack.getNumProductsInRack();
}
}
return count;
}
unsigned VendingMachine::countTill() const
{
unsigned count = 0;
for (size_t i = 0; i < numCoins; ++i)
{
count += coinBox[i]->getValueCents();
}
return count;
}
unsigned VendingMachine::getNumCoinsInCoinBox() const
{
return numCoins;
}
/*
* Vending Machine Project
* CSE-40477
*
* VendingMachine.h
* YOU MUST NOT CHANGE THIS FILE.
*/
#ifndef PROJECT1_VENDINGMACHINE_H
#define PROJECT1_VENDINGMACHINE_H
#include <ostream>
using std::ostream;
#include "Coin.h"
#include "DeliveryChute.h"
#include "Product.h"
#include "ProductButton.h"
#include "ProductRack.h"
#include "StatusPanel.h"
namespace Project1
{
//=========================================================================
// SUMMARY
// A vending machine containing products (soft drinks). Two types of
// users interact with a vending machine, customers and service
// professionals.
//
// A customer can insert coins into a coin slot, press any of the
// buttons to cause the appropriate product to be delivered if enough
// money has been inserted, retrieve a product from a delivery chute,
// and view a status panel to see diagnostic messages (e.g. not enough
// money was entered when a button was pressed, a button was pressed
// and the vending machine is out of the associated product, etc.).
//
// A service professional can add products to the vending machine,
// obtain a count of the number of products of a given type left in
// the machine, and obtain a sum of the total money in the machine.
//
// RESOURCES
// A vending machine owns (i.e. is responsible for destroying)
// all products and coins that are inserted into the machine as well
// as any other objects it internally creates. The vending machine
// relinquishes ownership of all products and coins that are removed
// from the vending machine.
//=========================================================================
class VendingMachine
{
public:
static const int NUM_PRODUCT_RACKS = 3;
static const int MAX_COINS = 200;
//---------------------------------------------------------------------
// SUMMARY
// Constructor. Creates an empty vending machine (containing no
// products or coins). The vending machine will be initialized to
// contain one product button connected to a product rack for each
// entry in the productNames list. The products stored in
// each rack will be priced according to the corresponding entry
// in the productPrices list (e.g. the products in the rack for
// productNames[0] will have the price given by productPrices[0]).
// The vending machine and its underlying components (specifically
// DeliveryChute and ProductRack) will output all error messages
// to a status panel initialized to write to statusPanelStream.
//
// RESOURCES
// The vending machine will be initialized to not own any product
// or coins.
//
// PARAMETERS
// statusPanelStream
// The ostream to which error messages will be written during
// the operation of the vending machine.
// productNames
// The names of all products that the vending machine can
// hold. A product rack will be created for each of these
// products.
// productPrices
// The prices of the products stored within the vending
// machine. Each entry in this list corresponds
// to the product at the same index in the productNames list.
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
VendingMachine(
ostream &statusPanelStream,
const char *productNames[NUM_PRODUCT_RACKS],
unsigned productPrices[NUM_PRODUCT_RACKS]);
//---------------------------------------------------------------------
// SUMMARY
// Destructor. Destroys the vending machine.
//
// RESOURCES
// All owned products, coins, and internally created objects are
// destroyed.
//
// PARAMETERS
// None
//
// RETURNS
// Nothing
//---------------------------------------------------------------------
~VendingMachine();
//---------------------------------------------------------------------
// SUMMARY
// Inserts a coin into the vending machine coin slot; this
// increases the current balance that can be used to purchase
// products. If the coin is invalid (a wooden nickel) the coin
// will be rejected and MESSAGE_CODE_BAD_COIN will be output to
// the status panel. It is safe to assume that no more than
// MAX_COINS will ever be inserted into the vending machine.
//
// RESOURCES
// The vending machine takes ownership of the coin if and only if
// the coin is accepted.
//
// PARAMETERS
// pCoin
// The coin to be inserted into the vending machine.
//
// RETURNS
// Whether the coin was successfully inserted into the vending
// machine.
//---------------------------------------------------------------------
bool insertCoin(Coin *pCoin);
//---------------------------------------------------------------------
// SUMMARY
// Presses the given button (the buttons are indexed starting with
// zero). If an invalid button was pressed
// MESSAGE_CODE_WRONG_BUTTON will be output to the stauts panel.
// If not enough money was inserted into the vending machine to
// purchase a product from the rack associated with the given
// button MESSAGECODE_INSUFFICIENT_MONEY will be output. If none
// of the cases occur the vending machine will delegate the remainder
// of the processing to the button.
//
// RESOURCES
// None
//
// PARAMETERS
// button
// The index of the button to be pressed.
//
// RETURNS
// Whether the press was succesful. If it was successful then
// the product from the appropriate rack will have been dropped
// into the delivery chute and the available balance will have
// been reduced by the cost of the product.
//---------------------------------------------------------------------
bool pressButton(int button);
//---------------------------------------------------------------------
// SUMMARY
// Retrieves the product currently sitting in the delivery chute.
//
// RESOURCES
// The vending machine relinquishes ownership of the product in
// the delivery chute.
//
// PARAMETERS
// None
//
// RETURNS
// The product currently in the delivery or 0 if no product is
// currently in the delivery chute.
//---------------------------------------------------------------------
Product *retrieveProduct();
//---------------------------------------------------------------------
// SUMMARY
// Add the product to the vending machine.
//
// RESOURCES
// The vending machine takes ownership of the product if and only
// if the product is accepted. If there is no product rack
// in the vending machine that is compatible with the product
// added then MESSAGECODE_NO_RACK_FOR_PRODUCT will be output to
// the status panel.
//
// PARAMETERS
// pProduct
// The product to be added to the vending machine.
//
// RETURNS
// Whether the product was successfully added to the vending
// machine.
//---------------------------------------------------------------------
bool addProduct(Product* pProduct);
//---------------------------------------------------------------------
// SUMMARY
// Return the number of products with the given name currently
// in the vending machine. If no product racks exist that are
// compatible with the given name zero will be returned.
//
// RESOURCES
// None
//
// PARAMETERS
// productName
// The name of the product for which to return a count.
//
// RETURNS
// The number of products with the given name currently in the
// vending machine. If no product racks exist that are
// compatible with the given name zero will be returned.
//---------------------------------------------------------------------
unsigned getProductCount(const char *productName) const;
//---------------------------------------------------------------------
// SUMMARY
// Return the sum of the values of all coins currently in the
// vending machine.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// The sum of the values of all coins currently in the vending
// machine.
//---------------------------------------------------------------------
unsigned countTill() const;
//---------------------------------------------------------------------
// SUMMARY
// Return the number of coins currently in the vending machine.
//
// RESOURCES
// None
//
// PARAMETERS
// None
//
// RETURNS
// The number of coins currently in the vending machine.
//---------------------------------------------------------------------
unsigned getNumCoinsInCoinBox() const;
private:
// A small display panel visible on the front of the vending machine.
// As customers and service professionals use the vending machine
// appropriate messages are displayed on this panel.
StatusPanel statusPanel;
// Delivery chute at the bottom front of the vending machine; when a
// product is purchased it is dropped into this delivery chute; the
// customer can then retrieve the product with her hand.
DeliveryChute deliveryChute;
// Buttons visible on the front of the vending machine that customers
// may push to purchase a product.
ProductButton *productButtons[NUM_PRODUCT_RACKS];
// Racks of products within the vending machine. Each rack is
// associated with a button; when the button is pressed the rack will
// release the next available product.
ProductRack *productRacks[NUM_PRODUCT_RACKS];
// All coins currently in the vending machine.
Coin *coinBox[MAX_COINS];
// Number of coins currently in the vending machine.
unsigned numCoins;
// Total monetary balance (in cents) remaining from coins that have
// been inserted into the vending machine but not been spent on
// purchasing products.
unsigned unspentMoneyCents;
};
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment