Basic testing done
authorNeil Smith <neil.git@njae.me.uk>
Sun, 9 Nov 2014 15:53:24 +0000 (15:53 +0000)
committerNeil Smith <neil.git@njae.me.uk>
Sun, 9 Nov 2014 15:53:24 +0000 (15:53 +0000)
app/src/androidTest/java/uk/me/njae/sunshine/FullTestSuite.java [new file with mode: 0644]
app/src/androidTest/java/uk/me/njae/sunshine/TestDb.java [new file with mode: 0644]
app/src/main/java/uk/me/njae/sunshine/data/WeatherContract.java [new file with mode: 0644]
app/src/main/java/uk/me/njae/sunshine/data/WeatherDbHelper.java [new file with mode: 0644]

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 (file)
index 0000000..fd4ca78
--- /dev/null
@@ -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 (file)
index 0000000..d47e534
--- /dev/null
@@ -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 (file)
index 0000000..99dd2d1
--- /dev/null
@@ -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 (file)
index 0000000..4c67404
--- /dev/null
@@ -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);
+    }
+}