2016-03-03 34 views
0

WeatherContract.java當assertTrue(locationId!= -1)Udacity陽光項目android testdb錯誤?

/* 
* Copyright (C) 2014 The Android Open Source Project 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
*  http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 
package com.example.android.sunshine.app.data; 

import android.provider.BaseColumns; 
import android.text.format.Time; 

/** 
* Defines table and column names for the weather database. 
*/ 
public class WeatherContract { 

    // To make it easy to query for the exact date, we normalize all dates that go into 
    // the database to the start of the the Julian day at UTC. 
    public static long normalizeDate(long startDate) { 
     // normalize the start date to the beginning of the (UTC) day 
     Time time = new Time(); 
     time.set(startDate); 
     int julianDay = Time.getJulianDay(startDate, time.gmtoff); 
     return time.setJulianDay(julianDay); 
    } 

    /* 
     Inner class that defines the table contents of the location table 
     Students: This is where you will add the strings. (Similar to what has been 
     done for WeatherEntry) 
    */ 
    public static final class LocationEntry implements BaseColumns { 
     public static final String TABLE_NAME = "location"; 

     public static final String COLUMN_LOCATION_SETTING = "location_setting"; 

     public static final String COLUMN_CITY_NAME = "city name"; 

     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 long in milliseconds since the epoch 
     public static final String COLUMN_DATE = "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"; 
    } 
} 

WeatherDbHelper.java

/* 
* Copyright (C) 2014 The Android Open Source Project 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
*  http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 
package com.example.android.sunshine.app.data; 

import android.content.Context; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteOpenHelper; 

import com.example.android.sunshine.app.data.WeatherContract.LocationEntry; 
import com.example.android.sunshine.app.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 = 3; 

    static final String DATABASE_NAME = "weather.db"; 

    public WeatherDbHelper(Context context) { 
     super(context, DATABASE_NAME, null, DATABASE_VERSION); 
    } 

    @Override 
    public void onCreate(SQLiteDatabase sqLiteDatabase) { 
     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, " + 


       // Inserting a new data with the same location setting will not update the table 
       // and is ignored. No row ID will be returned 
       "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_DATE + " INTEGER 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_DATE + ", " + 
       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) { 
     // This database is only a cache for online data, so its upgrade policy is 
     // to simply to discard the data and start over 
     // Note that this only fires if you change the version number for your database. 
     // It does NOT depend on the version number for your application. 
     // If you want to update the schema without wiping data, commenting out the next 2 lines 
     // should be your top priority before modifying this method. 
     sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + LocationEntry.TABLE_NAME); 
     sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + WeatherEntry.TABLE_NAME); 
     onCreate(sqLiteDatabase); 
    } 
} 

TestDb.java

/* 
* Copyright (C) 2014 The Android Open Source Project 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
*  http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 
package com.example.android.sunshine.app.data; 

import android.content.ContentValues; 
import android.database.Cursor; 
import android.database.sqlite.SQLiteDatabase; 
import android.test.AndroidTestCase; 

import java.util.HashSet; 

public class TestDb extends AndroidTestCase { 

    public static final String LOG_TAG = TestDb.class.getSimpleName(); 

    // Since we want each test to start with a clean slate 
    void deleteTheDatabase() { 
     mContext.deleteDatabase(WeatherDbHelper.DATABASE_NAME); 
    } 

    /* 
     This function gets called before each test is executed to delete the database. This makes 
     sure that we always have a clean test. 
    */ 
    public void setUp() { 
     deleteTheDatabase(); 
    } 

    /* 
     Students: Uncomment this test once you've written the code to create the Location 
     table. Note that you will have to have chosen the same column names that I did in 
     my solution for this test to compile, so if you haven't yet done that, this is 
     a good time to change your column names to match mine. 

     Note that this only tests that the Location table has the correct columns, since we 
     give you the code for the weather table. This test does not look at the 
    */ 
    public void testCreateDb() throws Throwable { 
     // build a HashSet of all of the table names we wish to look for 
     // Note that there will be another table in the DB that stores the 
     // Android metadata (db version information) 
     final HashSet<String> tableNameHashSet = new HashSet<String>(); 
     tableNameHashSet.add(WeatherContract.LocationEntry.TABLE_NAME); 
     tableNameHashSet.add(WeatherContract.WeatherEntry.TABLE_NAME); 

     mContext.deleteDatabase(WeatherDbHelper.DATABASE_NAME); 
     SQLiteDatabase db = new WeatherDbHelper(
       this.mContext).getWritableDatabase(); 
     assertEquals(true, db.isOpen()); 

     // have we created the tables we want? 
     Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null); 

     assertTrue("Error: This means that the database has not been created correctly", 
       c.moveToFirst()); 

     // verify that the tables have been created 
     do { 
      tableNameHashSet.remove(c.getString(0)); 
     } while(c.moveToNext()); 

     // if this fails, it means that your database doesn't contain both the location entry 
     // and weather entry tables 
     assertTrue("Error: Your database was created without both the location entry and weather entry tables", 
       tableNameHashSet.isEmpty()); 

     // now, do our tables contain the correct columns? 
     c = db.rawQuery("PRAGMA table_info(" + WeatherContract.LocationEntry.TABLE_NAME + ")", 
       null); 

     assertTrue("Error: This means that we were unable to query the database for table information.", 
       c.moveToFirst()); 

     // Build a HashSet of all of the column names we want to look for 
     final HashSet<String> locationColumnHashSet = new HashSet<String>(); 
     locationColumnHashSet.add(WeatherContract.LocationEntry._ID); 
     locationColumnHashSet.add(WeatherContract.LocationEntry.COLUMN_CITY_NAME); 
     locationColumnHashSet.add(WeatherContract.LocationEntry.COLUMN_COORD_LAT); 
     locationColumnHashSet.add(WeatherContract.LocationEntry.COLUMN_COORD_LONG); 
     locationColumnHashSet.add(WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING); 

     int columnNameIndex = c.getColumnIndex("name"); 
     do { 
      String columnName = c.getString(columnNameIndex); 
      locationColumnHashSet.remove(columnName); 
     } while(c.moveToNext()); 

     // if this fails, it means that your database doesn't contain all of the required location 
     // entry columns 
     assertTrue("Error: The database doesn't contain all of the required location entry columns", 
       locationColumnHashSet.isEmpty()); 
     db.close(); 
    } 

    /* 
     Students: Here is where you will build code to test that we can insert and query the 
     location database. We've done a lot of work for you. You'll want to look in TestUtilities 
     where you can uncomment out the "createNorthPoleLocationValues" function. You can 
     also make use of the ValidateCurrentRecord function from within TestUtilities. 
    */ 
    public void testLocationTable() { 
     long id = insertLocation(); 
    } 

    /* 
     Students: Here is where you will build code to test that we can insert and query the 
     database. We've done a lot of work for you. You'll want to look in TestUtilities 
     where you can use the "createWeatherValues" function. You can 
     also make use of the validateCurrentRecord function from within TestUtilities. 
    */ 
    public void testWeatherTable() { 
     long locationRowId = insertLocation(); 

     // First step: Get reference to writable database 
     SQLiteDatabase db = new WeatherDbHelper(this.mContext).getWritableDatabase(); 
     assertTrue("Error: failure to open the database for writing",db.isOpen()); 

     // Create ContentValues of what you want to insert 
     // (you can use the createWeatherValues TestUtilities function if you wish) 
     ContentValues testWeatherValues = TestUtilities.createWeatherValues(locationRowId); 

     // Insert ContentValues into database and get a row ID back 
     long weatherRowId = db.insert(WeatherContract.WeatherEntry.TABLE_NAME, null, testWeatherValues); 
     assertTrue("Error: failure to insert test weather values", weatherRowId != -1); 

     // Query the database and receive a Cursor back 
     Cursor weatherCursor = db.query(
       WeatherContract.WeatherEntry.TABLE_NAME, 
       null, 
       null, 
       null, 
       null, 
       null, 
       null 
     ); 

     // Move the cursor to a valid database row 
     assertTrue("Error: Test weather query returned no rows", weatherCursor.moveToFirst()); 

     // Validate data in resulting Cursor with the original ContentValues 
     // (you can use the validateCurrentRecord function in TestUtilities to validate the 
     // query if you like) 
     TestUtilities.validateCurrentRecord("Verifying test weather values failed", 
       weatherCursor, testWeatherValues); 

     assertFalse("Error: mMore than one row returned from location query", weatherCursor.moveToNext()); 

     // Finally, close the cursor and database 
     weatherCursor.close(); 
     db.close(); 
    } 


    /* 
     Students: This is a helper method for the testWeatherTable quiz. You can move your 
     code from testLocationTable to here so that you can call this code from both 
     testWeatherTable and testLocationTable. 
    */ 
    public long insertLocation() { 

     // First step: Get reference to writable database 
     SQLiteDatabase db = new WeatherDbHelper(this.mContext).getWritableDatabase(); 

     // Insert ContentValues into database and get a row ID back 
     ContentValues testValues = TestUtilities.createNorthPoleLocationValues(); 
     long rowId = db.insert(WeatherContract.LocationEntry.TABLE_NAME, null, testValues); 
     assertTrue("Error: failure to insert test location values", rowId != -1); 

     // Query the database and receive a Cursor back 
     Cursor locationCursor = db.query(
       WeatherContract.LocationEntry.TABLE_NAME, 
       null, 
       null, 
       null, 
       null, 
       null, 
       null 
     ); 

     // Move the cursor to a valid database row 
     assertTrue("Error: Test location query returned no rows", locationCursor.moveToFirst()); 

     // Validate data in resulting Cursor with the original ContentValues 
     // (you can use the validateCurrentRecord function in TestUtilities to validate the 
     // query if you like) 
     TestUtilities.validateCurrentRecord("Verifying test location values failed", 
       locationCursor, testValues); 

     assertFalse("Error: mMore than one row returned from location query", locationCursor.moveToNext()); 

     // Finally, close the cursor and database 
     locationCursor.close(); 
     db.close(); 
     return rowId; 
     //return -1; 
    } 
} 

的錯誤,這裏給了我是

03-03 16: 34:41.531 2010-2026/com.example .android.sunshine.app E/SQLiteLog:(1)near「name」:語法錯誤03-03 16:34:41.533 2010-2026/com.example.android.sunshine.app E/SQLiteDatabase:錯誤 插入城市名稱=北極coord_lat = 64.7488 coord_long = -147.353 location_setting = 99705 android.database.sqlite.SQLiteException:near「name」:語法錯誤 (code 1):,while compiling:INSERT INTO location(city name, coord_lat,coord_long,location_setting)VALUES(?,?,?,?) 在 android.database.sqlite.SQLiteConnection.nativePrepareStatement(本地 法) 在 android.database.sqlite.SQLiteConnection.ac quirePreparedStatement(SQLiteConnection.java:889) 在 android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:500) 在 android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588) 在機器人。 database.sqlite.SQLiteProgram。(SQLiteProgram.java:58) 在 android.database.sqlite.SQLiteStatement。(SQLiteStatement.java:31) 在 android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1469) at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1341) at com.example.android.sunshine.app.data.TestDb.insertLocation(TestDb.java:179) at com.example.android.sunshine.app.data.TestDb.testLocationTable(TestDb.java:115) 在java.lang.reflect.Method.invoke(本機方法) 在java.lang.reflect.Method.invoke(Method.java:372) 在junit.framework.TestCase.runTest(TestCase.java:168) 在junit.framework.TestResult.runProtected(TestResult.java:133) 在junit.framework.TestResult.run(TestResult.java:118) at junit.framework.TestCase.run(TestCase.java:124) at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191) at android .test.AndroidTestRunner.runTest(AndroidTestRunner.java:176) 在 android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555) 在 android.app.Instrumentation $ InstrumentationThread.run(Instrumentation.java:1837) 03-03 16:34:41.534 2010-2026/com.example.android.sunshine。應用 I/TestRunner的:失敗: testLocationTable(com.example.android.sunshine.app.data.TestDb)03-03 16:34:41.534 2010至2026年/ com.example.android.sunshine.app I/TestRunner的: -----開始異常----- 03-03 16:34:41.535 2010-2026/com.example.android.sunshine.app I/TestRunner:-----結束 異常--- - 03-03 16:34:41.536 2010-2026/com.example.android.sunshine.app I/TestRunner:已完成: testLocationTable(com.example.android.sunshine.app.data.TestDb)03-03 16:34:41.575 2010-2026/com.example.android.sunshine.app I/TestRunner: started: testWeatherTable(com.example.android.sunshine.app.data.TestDb)03-03 16:34 :42.095 2010-2026/com.example.android.sunsh ine.app E/SQLiteLog: (1)near「name」:語法錯誤03-03 16:34:42.097 2010-2026/com.example.android.sunshine.app E/SQLiteDatabase:錯誤 插入city name =北極coord_lat = 64.7488 coord_long = -147.353 location_setting = 99705 android.database.sqlite.SQLiteException:近 「名」:語法錯誤 (代碼1):在編譯:INSERT INTO所在地(市 名,coord_lat,coord_long ,location_setting)VALUES(?,?,?,?) 在 android.database.sqlite.SQLiteConnection.nativePrepareStatement(本地 法) 在 android.database.sqlite.SQLiteConnection.acquirePreparedS tatement(SQLiteConnection.java:889) 在 android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:500) 在 android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588) 在機器人。 database.sqlite.SQLiteProgram。(SQLiteProgram.java:58) 在 android.database.sqlite.SQLiteStatement。(SQLiteStatement.java:31) 在 android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1469) at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1341) 在 com.example.android.sunshine.app.data.TestDb.insertLocation(TestDb.java:179) at com.example.android.sunshine.app.data.TestDb.testWeatherTable(TestDb.java:125) 在java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at junit.framework.TestCase.runTest(TestCase.java:168) at junit.framework.TestCase.runBare(TestCase.java:134) at junit.framework.TestResult $ 1.protect(TestResult.java:115) at junit.framework.TestResult.runProtected(TestResult.java:133) 在junit.framework.TestResult.run(TestResult.java:118) 在junit.framework.TestCase.run(TestCase.java:124)

當我看着這些錯誤是因爲assertTrue(ROWID != -1)。不知何故,rowId結果是-1。我現在試了好幾個小時,不知道代碼中出了什麼問題。請幫助

+0

在*城市名稱添加下劃線*像你這樣的其他列名稱並重新安裝您的應用程序。 – Luksprog

+0

等一下。你是什​​麼意思?沒有下劃線怎麼了? –

+0

哦。我明白了,謝謝,我會嘗試 –

回答

0

您在「城市名稱」字段中有一個非法SQLite空間。嘗試用下劃線替換空格。

public static final String COLUMN_CITY_NAME = "city name"; 

public static final String COLUMN_CITY_NAME = "city_name"; 

有一個類似的問題/回答這裏

SQLite error when trying to Insert Values