Add logger (#131)

This commit is contained in:
Mikołaj Pich
2018-06-06 19:38:54 +02:00
committed by Rafał
parent 0e16519baf
commit a06d114127
31 changed files with 201 additions and 128 deletions

View File

@ -118,6 +118,9 @@ dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP"
implementation "com.google.android.gms:play-services-oss-licenses:$ossLicenses"
implementation "com.jakewharton.timber:timber:$timber"
implementation "at.favre.lib:slf4j-timber:$slf4jTimber"
implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") {
transitive = true
}

View File

@ -12,11 +12,12 @@ import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.support.DaggerApplication;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.utils.Log;
import io.fabric.sdk.android.Fabric;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.DaggerAppComponent;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.LoggerUtils;
import timber.log.Timber;
public class WulkanowyApp extends DaggerApplication {
@ -39,15 +40,18 @@ public class WulkanowyApp extends DaggerApplication {
if (repository.getSharedRepo().isUserLoggedIn()) {
try {
repository.getSyncRepo().initLastUser();
FabricUtils.logLogin("Open app", true);
} catch (Exception e) {
LogUtils.error("An error occurred when the application was started", e);
FabricUtils.logLogin("Open app", false);
Timber.e(e, "An error occurred when the application was started");
}
}
}
private void enableDebugLog() {
QueryBuilder.LOG_VALUES = true;
FlexibleAdapter.enableLogs(Log.Level.DEBUG);
FlexibleAdapter.enableLogs(eu.davidea.flexibleadapter.utils.Log.Level.DEBUG);
Timber.plant(new LoggerUtils.DebugLogTree());
}
private void initializeFabric() {
@ -60,6 +64,7 @@ public class WulkanowyApp extends DaggerApplication {
)
.debuggable(BuildConfig.DEBUG)
.build());
Timber.plant(new LoggerUtils.CrashlyticsTree());
}
@Override

View File

@ -22,7 +22,7 @@ import io.github.wulkanowy.data.db.dao.migrations.Migration26;
import io.github.wulkanowy.data.db.dao.migrations.Migration27;
import io.github.wulkanowy.data.db.dao.migrations.Migration28;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
@Singleton
public class DbHelper extends DaoMaster.OpenHelper {
@ -41,7 +41,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
LogUtils.info("Cleaning user data oldVersion=" + oldVersion + " newVersion=" + newVersion);
Timber.i("Cleaning user data oldVersion=%s newVersion=%s", oldVersion, newVersion);
Database database = new StandardDatabase(db);
recreateDatabase(database);
}
@ -54,11 +54,11 @@ public class DbHelper extends DaoMaster.OpenHelper {
for (Migration migration : migrations) {
if (oldVersion < migration.getVersion()) {
try {
LogUtils.info("Applying migration to db schema v" + migration.getVersion() + "...");
Timber.i("Applying migration to db schema v%s...", migration.getVersion());
migration.runMigration(db, sharedPref, vulcan);
LogUtils.info("Migration " + migration.getVersion() + " complete");
Timber.i("Migration %s complete", migration.getVersion());
} catch (Exception e) {
e.printStackTrace();
Timber.e(e, "Failed to apply migration");
recreateDatabase(db);
break;
}
@ -67,7 +67,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
}
private void recreateDatabase(Database db) {
LogUtils.info("Database is recreating...");
Timber.i("Database is recreating...");
sharedPref.setCurrentUserId(0);
DaoMaster.dropAllTables(db, true);
onCreate(db);

View File

@ -9,8 +9,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.github.wulkanowy.api.generic.Diary;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.generic.Diary;
import io.github.wulkanowy.data.db.dao.DbHelper;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.security.Scrambler;

View File

@ -3,8 +3,6 @@ package io.github.wulkanowy.data.db.resources;
import android.content.Context;
import android.content.res.Resources;
import com.crashlytics.android.Crashlytics;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
@ -16,8 +14,8 @@ import io.github.wulkanowy.R;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
import timber.log.Timber;
@Singleton
public class ResourcesRepository implements ResourcesContract {
@ -41,8 +39,7 @@ public class ResourcesRepository implements ResourcesContract {
@Override
public String getErrorLoginMessage(Exception exception) {
LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception);
Crashlytics.logException(exception);
Timber.e(exception,"%s encountered a error", AppConstant.APP_NAME);
if (exception instanceof CryptoException) {
return resources.getString(R.string.encrypt_failed_text);

View File

@ -24,9 +24,9 @@ import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Scrambler;
import timber.log.Timber;
@Singleton
public class AccountSync {
@ -73,7 +73,7 @@ public class AccountSync {
}
private Account insertAccount(String email, String password) throws CryptoException {
LogUtils.debug("Register account: " + email);
Timber.d("Register account");
Account account = new Account()
.setEmail(email)
.setPassword(Scrambler.encrypt(email, password, context));
@ -82,10 +82,11 @@ public class AccountSync {
}
private Symbol insertSymbol(Account account) throws VulcanException, IOException {
LogUtils.debug("Register symbol: " + vulcan.getSymbol());
String schoolId = vulcan.getStudentAndParent().getSchoolID();
Timber.d("Register symbol %s", vulcan.getSymbol());
Symbol symbol = new Symbol()
.setUserId(account.getId())
.setSchoolId(vulcan.getStudentAndParent().getSchoolID())
.setSchoolId(schoolId)
.setSymbol(vulcan.getSymbol());
daoSession.getSymbolDao().insert(symbol);
@ -97,7 +98,7 @@ public class AccountSync {
vulcan.getStudentAndParent().getStudents(),
symbol.getId()
);
LogUtils.debug("Register students: " + studentList.size());
Timber.d("Register students %s", studentList.size());
daoSession.getStudentDao().insertInTx(studentList);
}
@ -108,7 +109,7 @@ public class AccountSync {
StudentDao.Properties.SymbolId.eq(symbolEntity.getId()),
StudentDao.Properties.Current.eq(true)
).unique().getId());
LogUtils.debug("Register diaries: " + diaryList.size());
Timber.d("Register diaries %s", diaryList.size());
daoSession.getDiaryDao().insertInTx(diaryList);
}
@ -118,7 +119,7 @@ public class AccountSync {
daoSession.getDiaryDao().queryBuilder().where(
DiaryDao.Properties.Current.eq(true)
).unique().getId());
LogUtils.debug("Register semesters: " + semesterList.size());
Timber.d("Register semesters %s", semesterList.size());
daoSession.getSemesterDao().insertInTx(semesterList);
}
@ -130,7 +131,7 @@ public class AccountSync {
throw new NotRegisteredUserException("Can't find user id in SharedPreferences");
}
LogUtils.debug("Initialization current user id=" + userId);
Timber.d("Init current user (%s)", userId);
Account account = daoSession.getAccountDao().load(userId);

View File

@ -18,7 +18,7 @@ import io.github.wulkanowy.data.db.dao.entities.DayDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
@Singleton
public class AttendanceSync {
@ -47,7 +47,7 @@ public class AttendanceSync {
daoSession.getAttendanceLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization attendance lessons (amount = " + lessonList.size() + ")");
Timber.d("Attendance synchronization complete (%s)", lessonList.size());
}
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)

View File

@ -17,7 +17,7 @@ import io.github.wulkanowy.data.db.dao.entities.ExamDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
public class ExamsSync {
@ -45,7 +45,7 @@ public class ExamsSync {
daoSession.getExamDao().saveInTx(examList);
LogUtils.debug("Synchronization exams (amount = " + examList.size() + ")");
Timber.d("Exams synchronization complete (%s)", examList.size());
}
private Week getWeekFromDb(String date) {

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data.sync;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
@ -16,7 +15,7 @@ import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.SubjectDao;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.EntitiesCompare;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
@Singleton
public class GradeSync {
@ -33,7 +32,7 @@ public class GradeSync {
this.vulcan = vulcan;
}
public void sync(long semesterId) throws IOException, VulcanException, ParseException {
public void sync(long semesterId) throws IOException, VulcanException {
this.semesterId = semesterId;
Semester semester = daoSession.getSemesterDao().load(semesterId);
@ -44,7 +43,7 @@ public class GradeSync {
daoSession.getGradeDao().deleteInTx(semester.getGradeList());
daoSession.getGradeDao().insertInTx(lastList);
LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")");
Timber.d("Grades synchronization complete (%s)", lastList.size());
}
private void resetSemesterRelations(Semester semester) {
@ -64,7 +63,7 @@ public class GradeSync {
return updatedList;
}
private List<Grade> getComparedList(Semester semester) throws IOException, VulcanException, ParseException {
private List<Grade> getComparedList(Semester semester) throws IOException, VulcanException {
List<Grade> gradesFromNet = DataObjectConverter.gradesToGradeEntities(
vulcan.getGradesList().getAll(semester.getValue()), semesterId);

View File

@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
@Singleton
public class SubjectSync {
@ -40,7 +40,7 @@ public class SubjectSync {
daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb());
daoSession.getSubjectDao().insertInTx(lastList);
LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")");
Timber.d("Subjects synchronization complete (%s)", lastList.size());
}
private List<Subject> getSubjectsFromNet(Semester semester) throws VulcanException, IOException {

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data.sync;
import java.io.IOException;
import java.text.ParseException;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -47,17 +46,17 @@ public class SyncRepository implements SyncContract {
}
@Override
public void initLastUser() throws IOException, CryptoException {
public void initLastUser() throws CryptoException {
accountSync.initLastUser();
}
@Override
public void syncGrades(int semesterName) throws VulcanException, IOException, ParseException {
public void syncGrades(int semesterName) throws VulcanException, IOException {
gradeSync.sync(semesterName);
}
@Override
public void syncGrades() throws VulcanException, IOException, ParseException {
public void syncGrades() throws VulcanException, IOException {
gradeSync.sync(database.getCurrentSemesterId());
}
@ -72,12 +71,12 @@ public class SyncRepository implements SyncContract {
}
@Override
public void syncAttendance() throws ParseException, IOException, VulcanException {
public void syncAttendance() throws IOException, VulcanException {
attendanceSync.syncAttendance(database.getCurrentDiaryId(), null);
}
@Override
public void syncAttendance(long diaryId, String date) throws ParseException, IOException, VulcanException {
public void syncAttendance(long diaryId, String date) throws IOException, VulcanException {
if (diaryId != 0) {
attendanceSync.syncAttendance(diaryId, date);
} else {
@ -86,12 +85,12 @@ public class SyncRepository implements SyncContract {
}
@Override
public void syncTimetable() throws VulcanException, IOException, ParseException {
public void syncTimetable() throws VulcanException, IOException {
timetableSync.syncTimetable(database.getCurrentDiaryId(), null);
}
@Override
public void syncTimetable(long diaryId, String date) throws VulcanException, IOException, ParseException {
public void syncTimetable(long diaryId, String date) throws VulcanException, IOException {
if (diaryId != 0) {
timetableSync.syncTimetable(diaryId, date);
} else {
@ -100,12 +99,12 @@ public class SyncRepository implements SyncContract {
}
@Override
public void syncExams() throws VulcanException, IOException, ParseException {
public void syncExams() throws VulcanException, IOException {
examsSync.syncExams(database.getCurrentDiaryId(), null);
}
@Override
public void syncExams(long diaryId, String date) throws VulcanException, IOException, ParseException {
public void syncExams(long diaryId, String date) throws VulcanException, IOException {
if (diaryId != 0) {
examsSync.syncExams(diaryId, date);
} else {
@ -114,7 +113,7 @@ public class SyncRepository implements SyncContract {
}
@Override
public void syncAll() throws VulcanException, IOException, ParseException {
public void syncAll() throws VulcanException, IOException {
syncSubjects();
syncGrades();
syncAttendance();

View File

@ -20,7 +20,7 @@ import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
@Singleton
public class TimetableSync {
@ -49,7 +49,7 @@ public class TimetableSync {
daoSession.getTimetableLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization timetable lessons (amount = " + lessonList.size() + ")");
Timber.d("Timetable synchronization complete (%s)", lessonList.size());
}
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.timetable.TimetableDay> getWeekFromApi(String date)

View File

@ -4,7 +4,6 @@ import android.app.PendingIntent;
import android.content.Context;
import android.support.v4.app.NotificationCompat;
import com.crashlytics.android.Crashlytics;
import com.firebase.jobdispatcher.Constraint;
import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver;
@ -27,7 +26,8 @@ import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.sync.NotRegisteredUserException;
import io.github.wulkanowy.services.notifies.GradeNotify;
import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.FabricUtils;
import timber.log.Timber;
public class SyncJob extends SimpleJobService {
@ -74,13 +74,18 @@ public class SyncJob extends SimpleJobService {
if (!gradeList.isEmpty() && repository.getSharedRepo().isNotifyEnable()) {
showNotification();
}
FabricUtils.logLogin("Background", true);
return JobService.RESULT_SUCCESS;
} catch (NotRegisteredUserException e) {
logError(e);
stop(getApplicationContext());
return JobService.RESULT_FAIL_NORETRY;
} catch (Exception e) {
logError(e);
return JobService.RESULT_FAIL_RETRY;
}
}
@ -122,7 +127,7 @@ public class SyncJob extends SimpleJobService {
}
private void logError(Exception e) {
Crashlytics.logException(e);
LogUtils.error("During background synchronization an error occurred", e);
FabricUtils.logLogin("Background", false);
Timber.e(e, "During background synchronization an error occurred");
}
}

View File

@ -85,7 +85,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
@Override
public void onEndAsync(boolean success, Exception exception) {
if (success) {
FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol());
FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol(), "Success");
getView().openMainActivity();
return;
} else if (exception instanceof BadCredentialsException) {
@ -95,7 +95,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
getView().setErrorSymbolRequired();
getView().showSoftInput();
} else {
FabricUtils.logRegister(false, symbol);
FabricUtils.logRegister(false, symbol, exception.getMessage());
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
}

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.ui.main.exams;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
@ -13,7 +12,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;

View File

@ -4,7 +4,6 @@ import android.os.Bundle;
import javax.inject.Inject;
import io.github.wulkanowy.services.jobs.SyncJob;
import io.github.wulkanowy.services.notifies.NotificationService;
import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.login.LoginActivity;

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.utils;
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.answers.CustomEvent;
import com.crashlytics.android.answers.LoginEvent;
import com.crashlytics.android.answers.SignUpEvent;
public final class FabricUtils {
@ -10,17 +11,26 @@ public final class FabricUtils {
throw new IllegalStateException("Utility class");
}
public static void logRegister(boolean result, String symbol) {
public static void logLogin(String method, boolean result) {
Answers.getInstance().logLogin(new LoginEvent()
.putMethod(method)
.putSuccess(result)
);
}
public static void logRegister(boolean result, String symbol, String message) {
Answers.getInstance().logSignUp(new SignUpEvent()
.putMethod("Login activity")
.putSuccess(result)
.putCustomAttribute("symbol", symbol));
.putCustomAttribute("symbol", symbol)
.putCustomAttribute("message", message)
);
}
public static void logRefresh(String name, boolean result, String date) {
Answers.getInstance().logCustom(
new CustomEvent(name + " refresh")
.putCustomAttribute("Success", result ? 1 : 0)
.putCustomAttribute("Success", result ? "true" : "false")
.putCustomAttribute("Date", date)
);
}

View File

@ -1,26 +0,0 @@
package io.github.wulkanowy.utils;
import android.util.Log;
public final class LogUtils {
private LogUtils() {
throw new IllegalStateException("Utility class");
}
public static void debug(String message) {
Log.d(AppConstant.APP_NAME, message);
}
public static void error(String message, Throwable throwable) {
Log.e(AppConstant.APP_NAME, message, throwable);
}
public static void error(String message) {
Log.e(AppConstant.APP_NAME, message);
}
public static void info(String message) {
Log.i(AppConstant.APP_NAME, message);
}
}

View File

@ -0,0 +1,47 @@
package io.github.wulkanowy.utils;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import com.crashlytics.android.Crashlytics;
import timber.log.Timber;
public final class LoggerUtils {
public static class CrashlyticsTree extends Timber.Tree {
@Override
protected void log(int priority, @Nullable String tag, @Nullable String message, @Nullable Throwable t) {
Crashlytics.setInt("priority", priority);
Crashlytics.setString("tag", tag);
if (t == null) {
Crashlytics.log(message);
} else {
Crashlytics.setString("message", message);
Crashlytics.logException(t);
}
}
}
public static class DebugLogTree extends Timber.DebugTree {
@Override
protected void log(int priority, String tag, @NonNull String message, Throwable t) {
if ("HUAWEI".equals(Build.MANUFACTURER) || "samsung".equals(Build.MANUFACTURER)) {
if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) {
priority = Log.ERROR;
}
}
super.log(priority, AppConstant.APP_NAME, message, t);
}
@Override
protected String createStackElementTag(@NonNull StackTraceElement element) {
return super.createStackElementTag(element) + " - " + element.getLineNumber();
}
}
}

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.utils.async;
import android.os.AsyncTask;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
public class AbstractTask extends AsyncTask<Void, Integer, Boolean> {
@ -28,7 +28,7 @@ public class AbstractTask extends AsyncTask<Void, Integer, Boolean> {
} else if (onRefreshListener != null) {
onRefreshListener.onDoInBackgroundRefresh();
} else {
LogUtils.error("AbstractTask does not have a listener assigned");
Timber.e("AbstractTask does not have a listener assigned");
}
return true;
} catch (Exception e) {
@ -45,7 +45,7 @@ public class AbstractTask extends AsyncTask<Void, Integer, Boolean> {
} else if (onRefreshListener != null) {
onRefreshListener.onCanceledRefreshAsync();
} else {
LogUtils.error("AbstractTask does not have a listener assigned");
Timber.e("AbstractTask does not have a listener assigned");
}
}
@ -57,7 +57,7 @@ public class AbstractTask extends AsyncTask<Void, Integer, Boolean> {
} else if (onRefreshListener != null) {
onRefreshListener.onEndRefreshAsync(result, exception);
} else {
LogUtils.error("AbstractTask does not have a listener assigned");
Timber.e("AbstractTask does not have a listener assigned");
}
}
}

View File

@ -26,7 +26,7 @@ import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.security.auth.x500.X500Principal;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
public final class Scrambler {
@ -111,7 +111,7 @@ public final class Scrambler {
throw new CryptoException("GenerateNewKey - String is empty");
}
LogUtils.debug("Key pair are create");
Timber.d("Key pair are create");
}

View File

@ -3,8 +3,6 @@ package io.github.wulkanowy.data.db.dao.entities;
import org.junit.Assert;
import org.junit.Test;
import io.github.wulkanowy.R;
public class GradeTest {
@Test