From b529b0b07fd94118aff6add8b80ae53947819982 Mon Sep 17 00:00:00 2001 From: Neil Smith Date: Sun, 9 Nov 2014 15:53:24 +0000 Subject: [PATCH] Basic testing done --- .../uk/me/njae/sunshine/FullTestSuite.java | 21 +++ .../java/uk/me/njae/sunshine/TestDb.java | 159 ++++++++++++++++++ .../njae/sunshine/data/WeatherContract.java | 61 +++++++ .../njae/sunshine/data/WeatherDbHelper.java | 79 +++++++++ 4 files changed, 320 insertions(+) create mode 100644 app/src/androidTest/java/uk/me/njae/sunshine/FullTestSuite.java create mode 100644 app/src/androidTest/java/uk/me/njae/sunshine/TestDb.java create mode 100644 app/src/main/java/uk/me/njae/sunshine/data/WeatherContract.java create mode 100644 app/src/main/java/uk/me/njae/sunshine/data/WeatherDbHelper.java diff --git a/app/src/androidTest/java/uk/me/njae/sunshine/FullTestSuite.java b/app/src/androidTest/java/uk/me/njae/sunshine/FullTestSuite.java new file mode 100644 index 0000000..fd4ca78 --- /dev/null +++ b/app/src/androidTest/java/uk/me/njae/sunshine/FullTestSuite.java @@ -0,0 +1,21 @@ +package uk.me.njae.sunshine; + +import android.test.suitebuilder.TestSuiteBuilder; + +import junit.framework.Test; +import junit.framework.TestSuite; + + +/** + * Created by neil on 09/11/14. + */ +public class FullTestSuite extends TestSuite { + public static Test suite() { + return new TestSuiteBuilder(FullTestSuite.class) + .includeAllPackagesUnderHere().build(); + } + + public FullTestSuite() { + super(); + } +} diff --git a/app/src/androidTest/java/uk/me/njae/sunshine/TestDb.java b/app/src/androidTest/java/uk/me/njae/sunshine/TestDb.java new file mode 100644 index 0000000..d47e534 --- /dev/null +++ b/app/src/androidTest/java/uk/me/njae/sunshine/TestDb.java @@ -0,0 +1,159 @@ +package uk.me.njae.sunshine; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.test.AndroidTestCase; +import android.util.Log; + +import uk.me.njae.sunshine.data.WeatherContract.LocationEntry; +import uk.me.njae.sunshine.data.WeatherContract.WeatherEntry; +import uk.me.njae.sunshine.data.WeatherDbHelper; + +public class TestDb extends AndroidTestCase { + + public static final String LOG_TAG = TestDb.class.getSimpleName(); + + public void testCreateDb() throws Throwable { + mContext.deleteDatabase(WeatherDbHelper.DATABASE_NAME); + SQLiteDatabase db = new WeatherDbHelper( + this.mContext).getWritableDatabase(); + assertEquals(true, db.isOpen()); + db.close(); + } + + public void testInsertReadDb() { + + // Test data we're going to insert into the DB to see if it works. + String testLocationSetting = "99705"; + String testCityName = "North Pole"; + double testLatitude = 64.7488; + double testLongitude = -147.353; + + // If there's an error in those massive SQL table creation Strings, + // errors will be thrown here when you try to get a writable database. + WeatherDbHelper dbHelper = new WeatherDbHelper(mContext); + SQLiteDatabase db = dbHelper.getWritableDatabase(); + + // Create a new map of values, where column names are the keys + ContentValues values = new ContentValues(); + values.put(LocationEntry.COLUMN_LOCATION_SETTING, testLocationSetting); + values.put(LocationEntry.COLUMN_CITY_NAME, testCityName); + values.put(LocationEntry.COLUMN_COORD_LAT, testLatitude); + values.put(LocationEntry.COLUMN_COORD_LONG, testLongitude); + + long locationRowId; + locationRowId = db.insert(LocationEntry.TABLE_NAME, null, values); + + // Verify we got a row back. + assertTrue(locationRowId != -1); + Log.d(LOG_TAG, "New row id: " + locationRowId); + + // Data's inserted. IN THEORY. Now pull some out to stare at it and verify it made + // the round trip. + + // Specify which columns you want. + String[] columns = { + LocationEntry._ID, + LocationEntry.COLUMN_LOCATION_SETTING, + LocationEntry.COLUMN_CITY_NAME, + LocationEntry.COLUMN_COORD_LAT, + LocationEntry.COLUMN_COORD_LONG + }; + + // A cursor is your primary interface to the query results. + Cursor cursor = db.query( + LocationEntry.TABLE_NAME, // Table to Query + columns, + null, // Columns for the "where" clause + null, // Values for the "where" clause + null, // columns to group by + null, // columns to filter by row groups + null // sort order + ); + + // If possible, move to the first row of the query results. + if (cursor.moveToFirst()) { + // Get the value in each column by finding the appropriate column index. + int locationIndex = cursor.getColumnIndex(LocationEntry.COLUMN_LOCATION_SETTING); + String location = cursor.getString(locationIndex); + + int nameIndex = cursor.getColumnIndex((LocationEntry.COLUMN_CITY_NAME)); + String name = cursor.getString(nameIndex); + + int latIndex = cursor.getColumnIndex((LocationEntry.COLUMN_COORD_LAT)); + double latitude = cursor.getDouble(latIndex); + + int longIndex = cursor.getColumnIndex((LocationEntry.COLUMN_COORD_LONG)); + double longitude = cursor.getDouble(longIndex); + + // Hooray, data was returned! Assert that it's the right data, and that the database + // creation code is working as intended. + // Then take a break. We both know that wasn't easy. + assertEquals(testCityName, name); + assertEquals(testLocationSetting, location); + assertEquals(testLatitude, latitude); + assertEquals(testLongitude, longitude); + + // Fantastic. Now that we have a location, add some weather! + } else { + // That's weird, it works on MY machine... + fail("No values returned :("); + } + + // Fantastic. Now that we have a location, add some weather! + ContentValues weatherValues = new ContentValues(); + weatherValues.put(WeatherEntry.COLUMN_LOC_KEY, locationRowId); + weatherValues.put(WeatherEntry.COLUMN_DATETEXT, "20141205"); + weatherValues.put(WeatherEntry.COLUMN_DEGREES, 1.1); + weatherValues.put(WeatherEntry.COLUMN_HUMIDITY, 1.2); + weatherValues.put(WeatherEntry.COLUMN_PRESSURE, 1.3); + weatherValues.put(WeatherEntry.COLUMN_MAX_TEMP, 75); + weatherValues.put(WeatherEntry.COLUMN_MIN_TEMP, 65); + weatherValues.put(WeatherEntry.COLUMN_SHORT_DESC, "Asteroids"); + weatherValues.put(WeatherEntry.COLUMN_WIND_SPEED, 5.5); + weatherValues.put(WeatherEntry.COLUMN_WEATHER_ID, 321); + + long weatherRowId = db.insert(WeatherEntry.TABLE_NAME, null, weatherValues); + assertTrue(weatherRowId != -1); + + // A cursor is your primary interface to the query results. + Cursor weatherCursor = db.query( + WeatherEntry.TABLE_NAME, // Table to Query + null, // leaving "columns" null just returns all the columns. + null, // cols for "where" clause + null, // values for "where" clause + null, // columns to group by + null, // columns to filter by row groups + null // sort order + ); + + if (!weatherCursor.moveToFirst()) { + fail("No weather data returned!"); + } + + assertEquals(weatherCursor.getInt( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_LOC_KEY)), locationRowId); + assertEquals(weatherCursor.getString( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_DATETEXT)), "20141205"); + assertEquals(weatherCursor.getDouble( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_DEGREES)), 1.1); + assertEquals(weatherCursor.getDouble( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_HUMIDITY)), 1.2); + assertEquals(weatherCursor.getDouble( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_PRESSURE)), 1.3); + assertEquals(weatherCursor.getInt( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_MAX_TEMP)), 75); + assertEquals(weatherCursor.getInt( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_MIN_TEMP)), 65); + assertEquals(weatherCursor.getString( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_SHORT_DESC)), "Asteroids"); + assertEquals(weatherCursor.getDouble( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_WIND_SPEED)), 5.5); + assertEquals(weatherCursor.getInt( + weatherCursor.getColumnIndex(WeatherEntry.COLUMN_WEATHER_ID)), 321); + + weatherCursor.close(); + dbHelper.close(); + } +} diff --git a/app/src/main/java/uk/me/njae/sunshine/data/WeatherContract.java b/app/src/main/java/uk/me/njae/sunshine/data/WeatherContract.java new file mode 100644 index 0000000..99dd2d1 --- /dev/null +++ b/app/src/main/java/uk/me/njae/sunshine/data/WeatherContract.java @@ -0,0 +1,61 @@ +package uk.me.njae.sunshine.data; + +import android.provider.BaseColumns; + +/** + * Created by neil on 09/11/14. + */ +public class WeatherContract { + /* Inner class that defines the table contents of the location table */ + public static final class LocationEntry implements BaseColumns { + + // Table name + public static final String TABLE_NAME = "location"; + + // The location setting string is what will be sent to openweathermap + // as the location query. + public static final String COLUMN_LOCATION_SETTING = "location_setting"; + + // Human readable location string, provided by the API. Because for styling, + // "Mountain View" is more recognizable than 94043. + public static final String COLUMN_CITY_NAME = "city_name"; + + // In order to uniquely pinpoint the location on the map when we launch the + // map intent, we store the latitude and longitude as returned by openweathermap. + public static final String COLUMN_COORD_LAT = "coord_lat"; + public static final String COLUMN_COORD_LONG = "coord_long"; + } + + /* Inner class that defines the table contents of the weather table */ + public static final class WeatherEntry implements BaseColumns { + + public static final String TABLE_NAME = "weather"; + + // Column with the foreign key into the location table. + public static final String COLUMN_LOC_KEY = "location_id"; + // Date, stored as Text with format yyyy-MM-dd + public static final String COLUMN_DATETEXT = "date"; + // Weather id as returned by API, to identify the icon to be used + public static final String COLUMN_WEATHER_ID = "weather_id"; + + // Short description and long description of the weather, as provided by API. + // e.g "clear" vs "sky is clear". + public static final String COLUMN_SHORT_DESC = "short_desc"; + + // Min and max temperatures for the day (stored as floats) + public static final String COLUMN_MIN_TEMP = "min"; + public static final String COLUMN_MAX_TEMP = "max"; + + // Humidity is stored as a float representing percentage + public static final String COLUMN_HUMIDITY = "humidity"; + + // Humidity is stored as a float representing percentage + public static final String COLUMN_PRESSURE = "pressure"; + + // Windspeed is stored as a float representing windspeed mph + public static final String COLUMN_WIND_SPEED = "wind"; + + // Degrees are meteorological degrees (e.g, 0 is north, 180 is south). Stored as floats. + public static final String COLUMN_DEGREES = "degrees"; + } +} diff --git a/app/src/main/java/uk/me/njae/sunshine/data/WeatherDbHelper.java b/app/src/main/java/uk/me/njae/sunshine/data/WeatherDbHelper.java new file mode 100644 index 0000000..4c67404 --- /dev/null +++ b/app/src/main/java/uk/me/njae/sunshine/data/WeatherDbHelper.java @@ -0,0 +1,79 @@ +package uk.me.njae.sunshine.data; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import uk.me.njae.sunshine.data.WeatherContract.LocationEntry; +import uk.me.njae.sunshine.data.WeatherContract.WeatherEntry; + + +/** + * Manages a local database for weather data. + */ +public class WeatherDbHelper extends SQLiteOpenHelper { + + // If you change the database schema, you must increment the database version. + private static final int DATABASE_VERSION = 1; + + public static final String DATABASE_NAME = "weather.db"; + + public WeatherDbHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase sqLiteDatabase) { + // Create a table to hold locations. A location consists of the string supplied in the + // location setting, the city name, and the latitude and longitude + final String SQL_CREATE_LOCATION_TABLE = "CREATE TABLE " + LocationEntry.TABLE_NAME + " (" + + LocationEntry._ID + " INTEGER PRIMARY KEY," + + LocationEntry.COLUMN_LOCATION_SETTING + " TEXT UNIQUE NOT NULL, " + + LocationEntry.COLUMN_CITY_NAME + " TEXT NOT NULL, " + + LocationEntry.COLUMN_COORD_LAT + " REAL NOT NULL, " + + LocationEntry.COLUMN_COORD_LONG + " REAL NOT NULL, " + + "UNIQUE (" + LocationEntry.COLUMN_LOCATION_SETTING +") ON CONFLICT IGNORE"+ + " );"; + + final String SQL_CREATE_WEATHER_TABLE = "CREATE TABLE " + WeatherEntry.TABLE_NAME + " (" + + // Why AutoIncrement here, and not above? + // Unique keys will be auto-generated in either case. But for weather + // forecasting, it's reasonable to assume the user will want information + // for a certain date and all dates *following*, so the forecast data + // should be sorted accordingly. + WeatherEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + + // the ID of the location entry associated with this weather data + WeatherEntry.COLUMN_LOC_KEY + " INTEGER NOT NULL, " + + WeatherEntry.COLUMN_DATETEXT + " TEXT NOT NULL, " + + WeatherEntry.COLUMN_SHORT_DESC + " TEXT NOT NULL, " + + WeatherEntry.COLUMN_WEATHER_ID + " INTEGER NOT NULL," + + + WeatherEntry.COLUMN_MIN_TEMP + " REAL NOT NULL, " + + WeatherEntry.COLUMN_MAX_TEMP + " REAL NOT NULL, " + + + WeatherEntry.COLUMN_HUMIDITY + " REAL NOT NULL, " + + WeatherEntry.COLUMN_PRESSURE + " REAL NOT NULL, " + + WeatherEntry.COLUMN_WIND_SPEED + " REAL NOT NULL, " + + WeatherEntry.COLUMN_DEGREES + " REAL NOT NULL, " + + + // Set up the location column as a foreign key to location table. + " FOREIGN KEY (" + WeatherEntry.COLUMN_LOC_KEY + ") REFERENCES " + + LocationEntry.TABLE_NAME + " (" + LocationEntry._ID + "), " + + + // To assure the application have just one weather entry per day + // per location, it's created a UNIQUE constraint with REPLACE strategy + " UNIQUE (" + WeatherEntry.COLUMN_DATETEXT + ", " + + WeatherEntry.COLUMN_LOC_KEY + ") ON CONFLICT REPLACE);"; + + sqLiteDatabase.execSQL(SQL_CREATE_LOCATION_TABLE); + sqLiteDatabase.execSQL(SQL_CREATE_WEATHER_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { + sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + LocationEntry.TABLE_NAME); + sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + WeatherEntry.TABLE_NAME); + onCreate(sqLiteDatabase); + } +} -- 2.34.1