mirror of
https://github.com/hmalik144/Android-Cucumber-BDD-Sample.git
synced 2025-12-09 23:45:19 +00:00
Initial commit
This commit is contained in:
41
.gitignore
vendored
Normal file
41
.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Built application files
|
||||||
|
*.apk
|
||||||
|
*.ap_
|
||||||
|
|
||||||
|
# Files for the ART/Dalvik VM
|
||||||
|
*.dex
|
||||||
|
|
||||||
|
# Java class files
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
bin/
|
||||||
|
gen/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Gradle files
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Local configuration file (sdk path, etc)
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
# Proguard folder generated by Eclipse
|
||||||
|
proguard/
|
||||||
|
|
||||||
|
# Log Files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Android Studio Navigation editor temp files
|
||||||
|
.navigation/
|
||||||
|
|
||||||
|
# Android Studio captures folder
|
||||||
|
captures/
|
||||||
|
|
||||||
|
# Intellij
|
||||||
|
*.iml
|
||||||
|
.idea/workspace.xml
|
||||||
|
.idea/libraries
|
||||||
|
|
||||||
|
# Keystore files
|
||||||
|
*.jks
|
||||||
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
BookLibrary
|
||||||
22
.idea/compiler.xml
generated
Normal file
22
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<resourceExtensions />
|
||||||
|
<wildcardResourcePatterns>
|
||||||
|
<entry name="!?*.java" />
|
||||||
|
<entry name="!?*.form" />
|
||||||
|
<entry name="!?*.class" />
|
||||||
|
<entry name="!?*.groovy" />
|
||||||
|
<entry name="!?*.scala" />
|
||||||
|
<entry name="!?*.flex" />
|
||||||
|
<entry name="!?*.kt" />
|
||||||
|
<entry name="!?*.clj" />
|
||||||
|
<entry name="!?*.aj" />
|
||||||
|
</wildcardResourcePatterns>
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile default="true" name="Default" enabled="false">
|
||||||
|
<processorPath useClasspath="true" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.idea/copyright/profiles_settings.xml
generated
Normal file
3
.idea/copyright/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings default="" />
|
||||||
|
</component>
|
||||||
6
.idea/encodings.xml
generated
Normal file
6
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="PROJECT" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
23
.idea/gradle.xml
generated
Normal file
23
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="myModules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
62
.idea/misc.xml
generated
Normal file
62
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="EntryPointsManager">
|
||||||
|
<entry_points version="2.0" />
|
||||||
|
</component>
|
||||||
|
<component name="NullableNotNullManager">
|
||||||
|
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||||
|
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||||
|
<option name="myNullables">
|
||||||
|
<value>
|
||||||
|
<list size="4">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="myNotNulls">
|
||||||
|
<value>
|
||||||
|
<list size="4">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
|
||||||
|
<OptionsSetting value="true" id="Add" />
|
||||||
|
<OptionsSetting value="true" id="Remove" />
|
||||||
|
<OptionsSetting value="true" id="Checkout" />
|
||||||
|
<OptionsSetting value="true" id="Update" />
|
||||||
|
<OptionsSetting value="true" id="Status" />
|
||||||
|
<OptionsSetting value="true" id="Edit" />
|
||||||
|
<ConfirmationsSetting value="0" id="Add" />
|
||||||
|
<ConfirmationsSetting value="0" id="Remove" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
<component name="masterDetails">
|
||||||
|
<states>
|
||||||
|
<state key="ProjectJDKs.UI">
|
||||||
|
<settings>
|
||||||
|
<last-edited>1.8</last-edited>
|
||||||
|
<splitter-proportions>
|
||||||
|
<option name="proportions">
|
||||||
|
<list>
|
||||||
|
<option value="0.2" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</splitter-proportions>
|
||||||
|
</settings>
|
||||||
|
</state>
|
||||||
|
</states>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/modules.xml
generated
Normal file
9
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/BookLibrary.iml" filepath="$PROJECT_DIR$/BookLibrary.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/runConfigurations.xml
generated
Normal file
12
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
35
.idea/runConfigurations/All_Instrumentation_Tests.xml
generated
Normal file
35
.idea/runConfigurations/All_Instrumentation_Tests.xml
generated
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All Instrumentation Tests" type="AndroidTestRunConfigurationType" factoryName="Android Tests">
|
||||||
|
<module name="app" />
|
||||||
|
<option name="TESTING_TYPE" value="1" />
|
||||||
|
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="CLASS_NAME" value="com.codemate.booklibrary.ui.MainActivityTest" />
|
||||||
|
<option name="PACKAGE_NAME" value="com.codemate.booklibrary" />
|
||||||
|
<option name="EXTRA_OPTIONS" value="" />
|
||||||
|
<option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
|
||||||
|
<option name="PREFERRED_AVD" value="" />
|
||||||
|
<option name="CLEAR_LOGCAT" value="false" />
|
||||||
|
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="true" />
|
||||||
|
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
|
||||||
|
<option name="FORCE_STOP_RUNNING_APP" value="true" />
|
||||||
|
<option name="DEBUGGER_TYPE" value="Java" />
|
||||||
|
<option name="USE_LAST_SELECTED_DEVICE" value="true" />
|
||||||
|
<option name="PREFERRED_AVD" value="" />
|
||||||
|
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="2147483647" />
|
||||||
|
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||||
|
<Native>
|
||||||
|
<option name="WORKING_DIR" value="" />
|
||||||
|
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||||
|
</Native>
|
||||||
|
<Java />
|
||||||
|
<Hybrid>
|
||||||
|
<option name="WORKING_DIR" value="" />
|
||||||
|
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||||
|
</Hybrid>
|
||||||
|
<Profilers>
|
||||||
|
<option name="GAPID_DISABLE_PCS" value="false" />
|
||||||
|
</Profilers>
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
7
.idea/runConfigurations/All_Tests.xml
generated
Normal file
7
.idea/runConfigurations/All_Tests.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All Tests" type="CompoundRunConfigurationType" factoryName="Compound Run Configuration">
|
||||||
|
<toRun type="AndroidTestRunConfigurationType" name="All Instrumentation Tests" />
|
||||||
|
<toRun type="JUnit" name="All Unit Tests" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
29
.idea/runConfigurations/All_Unit_Tests.xml
generated
Normal file
29
.idea/runConfigurations/All_Unit_Tests.xml
generated
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All Unit Tests" type="JUnit" factoryName="JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea">
|
||||||
|
<pattern>
|
||||||
|
<option name="PATTERN" value="com.codemate.booklibrary.ui.*" />
|
||||||
|
<option name="ENABLED" value="true" />
|
||||||
|
</pattern>
|
||||||
|
</extension>
|
||||||
|
<module name="app" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="com.codemate.booklibrary" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="directory" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$MODULE_DIR$" />
|
||||||
|
<option name="ENV_VARIABLES" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<envs />
|
||||||
|
<dir value="$PROJECT_DIR$/app/src/test" />
|
||||||
|
<patterns />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
65
README.md
Normal file
65
README.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Behavior-Driven Development with Cucumber
|
||||||
|
|
||||||
|
This is a sample Android app that has tests mostly written using the Cucumber framework. The app acts like a library book search, allowing you to search for books by title, author or year. **The test coverage is 97%**.
|
||||||
|
|
||||||
|
[Cucumber](https://cucumber.io/) is a BDD testing framework that allows people without programming background write specifications **that can be translated to unit tests almost automatically**.
|
||||||
|
|
||||||
|
Writing tests turns more fun and it's no more a pain in the ass to write them first. It's also easier to specify the requirements.
|
||||||
|
|
||||||
|
## An example
|
||||||
|
|
||||||
|
The Oulu City Library has an application for book management.
|
||||||
|
|
||||||
|
The application is working fine, but they don't have a search feature. For the first version of the search feature, there's only one thing to implement:
|
||||||
|
|
||||||
|
* the employees must be able to search for books by authors.
|
||||||
|
|
||||||
|
This translates to the following **Gherkin** syntax, which could be even written by the customer or project lead.
|
||||||
|
|
||||||
|
**booksearch.feature:**
|
||||||
|
|
||||||
|
```gherkin
|
||||||
|
Feature: Book Search
|
||||||
|
|
||||||
|
Scenario: Search books by author
|
||||||
|
Given there's a book called "Tips for horrible hangovers" written by "John Smith"
|
||||||
|
And there's a book called "Bananas and their many colors" written by "James Brown"
|
||||||
|
And there's a book called "Mama look I'm a rock star" written by "John Smith"
|
||||||
|
When an employee searches by author "John Smith"
|
||||||
|
Then 2 books should be found
|
||||||
|
And Book 1 has the title "Tips for horrible hangovers"
|
||||||
|
And Book 2 has the title "Mama look I'm a rock star"
|
||||||
|
```
|
||||||
|
|
||||||
|
Simple, isn't it? This can easily be read and understood by non-programmers!
|
||||||
|
|
||||||
|
From that file, Cucumber pretty much autogenerates the Regular Expressions and Annotations needed to match the specific criterias. Then we only need to add the test logic and if we want, modify the method names.
|
||||||
|
|
||||||
|
|
||||||
|
**BookSearchSteps.java (annotations and methods auto-generated):**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class BookSearchSteps {
|
||||||
|
private Library library = new Library();
|
||||||
|
private List<Book> searchResults = new ArrayList<>();
|
||||||
|
|
||||||
|
@Given("^there's a book called \"([^\"]*)\" written by \"([^\"]*)\"$")
|
||||||
|
public void addBook(String title, String author) throws Throwable {
|
||||||
|
Book book = new Book(title, author);
|
||||||
|
library.addBook(book);
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^an employee searches by author \"([^\"]*)\"$")
|
||||||
|
public void searchForAuthor(String authorQuery) throws Throwable {
|
||||||
|
searchResults = library.findBooksByAuthor(authorQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
@And("^Book (\\d+) has the title \"([^\"]*)\"$")
|
||||||
|
public void verifyBookFound(int position, String title) throws Throwable {
|
||||||
|
int realPosition = position - 1;
|
||||||
|
assertEquals(title, searchResults.get(realPosition).getTitle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now that we have the tests in place, we just implement the functionality to make the tests pass!
|
||||||
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
41
app/build.gradle
Normal file
41
app/build.gradle
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 24
|
||||||
|
buildToolsVersion "23.0.3"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.codemate.booklibrary"
|
||||||
|
minSdkVersion 19
|
||||||
|
targetSdkVersion 24
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
testCoverageEnabled true
|
||||||
|
}
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
|
androidTestCompile ('com.android.support.test.espresso:espresso-core:2.2.1') {
|
||||||
|
exclude module: 'support-annotations'
|
||||||
|
}
|
||||||
|
|
||||||
|
testCompile 'junit:junit:4.12'
|
||||||
|
testCompile 'info.cukes:cucumber-java:1.2.4'
|
||||||
|
testCompile 'info.cukes:cucumber-junit:1.2.4'
|
||||||
|
testCompile 'org.mockito:mockito-core:1.+'
|
||||||
|
|
||||||
|
compile 'com.android.support:appcompat-v7:24.2.0'
|
||||||
|
compile 'com.android.support:recyclerview-v7:24.2.0'
|
||||||
|
}
|
||||||
17
app/proguard-rules.pro
vendored
Normal file
17
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in /Users/ironman/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.codemate.booklibrary;
|
||||||
|
|
||||||
|
import android.support.test.espresso.NoMatchingViewException;
|
||||||
|
import android.support.test.espresso.ViewAssertion;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public class RecyclerViewAssertions {
|
||||||
|
|
||||||
|
public static ViewAssertion adapterItemCountEquals(int count) {
|
||||||
|
return new ItemCountAssertion(ItemCountAssertion.MODE_EQUALS, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ViewAssertion adapterItemCountLowerThan(int count) {
|
||||||
|
return new ItemCountAssertion(ItemCountAssertion.MODE_LESS_THAN, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ItemCountAssertion implements ViewAssertion {
|
||||||
|
private static final int MODE_EQUALS = 0;
|
||||||
|
private static final int MODE_LESS_THAN = 1;
|
||||||
|
|
||||||
|
private final int mode;
|
||||||
|
private final int expectedChildCount;
|
||||||
|
|
||||||
|
ItemCountAssertion(int mode, int exptectedChildCount) {
|
||||||
|
this.mode = mode;
|
||||||
|
this.expectedChildCount = exptectedChildCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void check (View view, NoMatchingViewException noViewFoundException) {
|
||||||
|
if (noViewFoundException != null) {
|
||||||
|
throw noViewFoundException;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecyclerView recyclerView = (RecyclerView) view;
|
||||||
|
RecyclerView.Adapter adapter = recyclerView.getAdapter();
|
||||||
|
|
||||||
|
if (mode == MODE_EQUALS) {
|
||||||
|
assertThat(expectedChildCount, is(adapter.getItemCount()));
|
||||||
|
} else if (mode == MODE_LESS_THAN) {
|
||||||
|
assertTrue(expectedChildCount > adapter.getItemCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.codemate.booklibrary.ui;
|
||||||
|
|
||||||
|
import android.support.test.espresso.action.ViewActions;
|
||||||
|
import android.support.test.espresso.matcher.ViewMatchers;
|
||||||
|
import android.support.test.rule.ActivityTestRule;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import com.codemate.booklibrary.R;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static android.support.test.espresso.Espresso.onView;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||||
|
import static com.codemate.booklibrary.RecyclerViewAssertions.adapterItemCountEquals;
|
||||||
|
import static com.codemate.booklibrary.RecyclerViewAssertions.adapterItemCountLowerThan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class MainActivityTest {
|
||||||
|
@Rule
|
||||||
|
public ActivityTestRule<MainActivity> mainActivityTestRule =
|
||||||
|
new ActivityTestRule<>(MainActivity.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void allBooksDisplayedInUI() {
|
||||||
|
onView(ViewMatchers.withId(R.id.bookRecycler))
|
||||||
|
.check(adapterItemCountEquals(45));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onSearchTermEntered_NonMatchingItemsNotShown() {
|
||||||
|
onView(isAssignableFrom(EditText.class))
|
||||||
|
.perform(ViewActions.typeText("2"));
|
||||||
|
onView(withId(R.id.bookRecycler))
|
||||||
|
.check(adapterItemCountLowerThan(45));
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/src/main/AndroidManifest.xml
Normal file
20
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.codemate.booklibrary">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
<activity android:name=".ui.MainActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
30
app/src/main/java/com/codemate/booklibrary/data/Book.java
Normal file
30
app/src/main/java/com/codemate/booklibrary/data/Book.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package com.codemate.booklibrary.data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 01/09/16.
|
||||||
|
*/
|
||||||
|
public class Book {
|
||||||
|
private final String title;
|
||||||
|
private final String author;
|
||||||
|
private final Date publishDate;
|
||||||
|
|
||||||
|
public Book(String title, String author, Date publishDate) {
|
||||||
|
this.title = title;
|
||||||
|
this.author = author;
|
||||||
|
this.publishDate = publishDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getPublishDate() {
|
||||||
|
return publishDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
95
app/src/main/java/com/codemate/booklibrary/data/Library.java
Normal file
95
app/src/main/java/com/codemate/booklibrary/data/Library.java
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package com.codemate.booklibrary.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 01/09/16.
|
||||||
|
*/
|
||||||
|
public class Library {
|
||||||
|
private List<Book> inventory = new ArrayList<>();
|
||||||
|
|
||||||
|
public void addBook(Book book) {
|
||||||
|
inventory.add(book);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBooks(List<Book> books) {
|
||||||
|
inventory.addAll(books);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Book> getAllBooks() {
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a stupid search. In a real scenario, we would have something that
|
||||||
|
* would be much better.
|
||||||
|
*/
|
||||||
|
public List<Book> search(String searchQuery) {
|
||||||
|
List<Book> results = new ArrayList<>();
|
||||||
|
|
||||||
|
if (searchQuery.matches("^[1-9][0-9]{0,3}$")) {
|
||||||
|
results.addAll(findBooksByYear(searchQuery));
|
||||||
|
}
|
||||||
|
|
||||||
|
results.addAll(findBooksByAuthor(searchQuery));
|
||||||
|
results.addAll(findBooksByTitle(searchQuery));
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Book> findBooksByAuthor(String authorQuery) {
|
||||||
|
List<Book> results = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Book candidate : inventory) {
|
||||||
|
String normalizedAuthor = candidate.getAuthor().toLowerCase();
|
||||||
|
String normalizedSearchQuery = authorQuery.toLowerCase();
|
||||||
|
|
||||||
|
if (normalizedAuthor.contains(normalizedSearchQuery)) {
|
||||||
|
results.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Book> findBooksByTitle(String titleQuery) {
|
||||||
|
List<Book> results = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Book candidate : inventory) {
|
||||||
|
String normalizedTitle = candidate.getTitle().toLowerCase();
|
||||||
|
String normalizedSearchQuery = titleQuery.toLowerCase();
|
||||||
|
|
||||||
|
if (normalizedTitle.contains(normalizedSearchQuery)) {
|
||||||
|
results.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Book> findBooksByYear(String searchedYear) {
|
||||||
|
List<Book> results = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Book candidate : inventory) {
|
||||||
|
Calendar candidateCalendar = Calendar.getInstance();
|
||||||
|
candidateCalendar.setTime(candidate.getPublishDate());
|
||||||
|
|
||||||
|
int candidateYear = candidateCalendar.get(Calendar.YEAR);
|
||||||
|
|
||||||
|
if (candidateYear == Integer.valueOf(searchedYear)
|
||||||
|
|| yearsStartSimilarly(candidateYear, searchedYear)) {
|
||||||
|
results.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean yearsStartSimilarly(int candidateYear, String searchedYear) {
|
||||||
|
String candidate = String.valueOf(candidateYear);
|
||||||
|
|
||||||
|
return candidate.startsWith(searchedYear);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.codemate.booklibrary.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public class RandomBookGenerator {
|
||||||
|
private RandomBookGenerator() {}
|
||||||
|
|
||||||
|
private static final String[] SUBJECTS = {
|
||||||
|
"Chicken", "Pig", "Hippo", "Dinosaur", "Giraffe", "Orangutan",
|
||||||
|
"Bat", "Lion", "Daddy", "Grandpa", "Donald Trump", "Coffee",
|
||||||
|
"Dog", "Rat", "Pokemon", "Cat", "Senile", "Pensioner",
|
||||||
|
"Project manager", "Salesperson"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] SECOND_WORDS = {
|
||||||
|
"strip club", "that knew too little", "Strategy", "parking space",
|
||||||
|
"Office", "Banana", "Toilet", "Poop", "Fart", "Rain", "Job",
|
||||||
|
"Thing", "Radio", "Zoo", "Office", "House", "Village",
|
||||||
|
"swimming pool", "Computer"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] FIRST_NAMES = {
|
||||||
|
"Bob", "Anna", "Tim", "Bethany", "Donald", "Mickey", "John",
|
||||||
|
"George", "Dick", "James", "Timothy", "Joanne", "Angus"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] LAST_NAMES = {
|
||||||
|
"Doe", "Trump", "Bond", "Jackson", "Jordan", "Young", "Page",
|
||||||
|
"Johnson", "Springsteen", "Kauffman", "Schmidt", "Jokinen"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static List<Book> randomBooks(int howMany) {
|
||||||
|
List<Book> books = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < howMany; i++) {
|
||||||
|
books.add(randomBook());
|
||||||
|
}
|
||||||
|
|
||||||
|
return books;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Book randomBook() {
|
||||||
|
String title = randomTitle();
|
||||||
|
String author = randomAuthor();
|
||||||
|
Date date = randomDate();
|
||||||
|
|
||||||
|
return new Book(title, author, date);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String randomTitle() {
|
||||||
|
String subject = RandomUtils.randomFromArray(SUBJECTS);
|
||||||
|
String secondWord = RandomUtils.randomFromArray(SECOND_WORDS);
|
||||||
|
|
||||||
|
return subject + " " + secondWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String randomAuthor() {
|
||||||
|
String firstName = RandomUtils.randomFromArray(FIRST_NAMES);
|
||||||
|
String lastName = RandomUtils.randomFromArray(LAST_NAMES);
|
||||||
|
|
||||||
|
return firstName + " " + lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Date randomDate() {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.set(Calendar.YEAR, RandomUtils.randBetween(1995, 2016));
|
||||||
|
calendar.set(Calendar.MONTH, RandomUtils.randBetween(0, 11));
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, RandomUtils.randBetween(1, 25));
|
||||||
|
|
||||||
|
return calendar.getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.codemate.booklibrary.data;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public class RandomUtils {
|
||||||
|
private RandomUtils() {}
|
||||||
|
|
||||||
|
private static final Random random;
|
||||||
|
|
||||||
|
static {
|
||||||
|
random = new Random();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T randomFromArray(T[] array) {
|
||||||
|
return array[randBetween(0, array.length - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int randBetween(int start, int end) {
|
||||||
|
return start + (int) Math.round(random.nextDouble() * (end - start));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.codemate.booklibrary.ui;
|
||||||
|
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.codemate.booklibrary.data.Book;
|
||||||
|
import com.codemate.booklibrary.R;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public class BookAdapter extends RecyclerView.Adapter<BookAdapter.ViewHolder> {
|
||||||
|
private final SimpleDateFormat dateFormat;
|
||||||
|
private List<Book> items = new ArrayList<>();
|
||||||
|
|
||||||
|
public BookAdapter() {
|
||||||
|
dateFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||||
|
View itemView = inflater.inflate(R.layout.item_book, parent, false);
|
||||||
|
|
||||||
|
return new ViewHolder(itemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
|
Book book = items.get(position);
|
||||||
|
|
||||||
|
holder.title.setText(book.getTitle());
|
||||||
|
holder.author.setText(book.getAuthor());
|
||||||
|
holder.date.setText(dateFormat.format(book.getPublishDate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateItems(List<Book> books) {
|
||||||
|
this.items = books;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
final TextView title;
|
||||||
|
final TextView author;
|
||||||
|
final TextView date;
|
||||||
|
|
||||||
|
public ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
title = (TextView) itemView.findViewById(R.id.bookTitle);
|
||||||
|
author = (TextView) itemView.findViewById(R.id.bookAuthor);
|
||||||
|
date = (TextView) itemView.findViewById(R.id.bookDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.codemate.booklibrary.ui;
|
||||||
|
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.SearchView;
|
||||||
|
|
||||||
|
import com.codemate.booklibrary.data.Book;
|
||||||
|
import com.codemate.booklibrary.data.Library;
|
||||||
|
import com.codemate.booklibrary.R;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity implements MainView, SearchView.OnQueryTextListener {
|
||||||
|
|
||||||
|
private MainPresenter presenter;
|
||||||
|
private BookAdapter bookAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
initializeViews();
|
||||||
|
|
||||||
|
presenter = new MainPresenter(this, new Library());
|
||||||
|
presenter.loadAllBooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeViews() {
|
||||||
|
bookAdapter = new BookAdapter();
|
||||||
|
|
||||||
|
RecyclerView bookRecycler = (RecyclerView) findViewById(R.id.bookRecycler);
|
||||||
|
bookRecycler.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
bookRecycler.setAdapter(bookAdapter);
|
||||||
|
|
||||||
|
SearchView searchView = (SearchView) findViewById(R.id.searchView);
|
||||||
|
searchView.setOnQueryTextListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
presenter.searchForBooks(newText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showBooks(List<Book> books) {
|
||||||
|
bookAdapter.updateItems(books);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.codemate.booklibrary.ui;
|
||||||
|
|
||||||
|
import com.codemate.booklibrary.data.Book;
|
||||||
|
import com.codemate.booklibrary.data.Library;
|
||||||
|
import com.codemate.booklibrary.data.RandomBookGenerator;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public class MainPresenter {
|
||||||
|
private final MainView mainView;
|
||||||
|
private final Library library;
|
||||||
|
|
||||||
|
public MainPresenter(MainView mainView, Library library) {
|
||||||
|
this.mainView = mainView;
|
||||||
|
this.library = library;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void searchForBooks(String searchQuery) {
|
||||||
|
List<Book> searchResults = library.search(searchQuery);
|
||||||
|
loadBooks(searchResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadAllBooks() {
|
||||||
|
// Populate the library with fake dummy data. In a real app
|
||||||
|
// we would have an interactor that would fetch the books from
|
||||||
|
// a real API.
|
||||||
|
List<Book> books = RandomBookGenerator.randomBooks(45);
|
||||||
|
library.addBooks(books);
|
||||||
|
|
||||||
|
loadBooks(library.getAllBooks());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadBooks(List<Book> books) {
|
||||||
|
mainView.showBooks(books);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
app/src/main/java/com/codemate/booklibrary/ui/MainView.java
Normal file
12
app/src/main/java/com/codemate/booklibrary/ui/MainView.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package com.codemate.booklibrary.ui;
|
||||||
|
|
||||||
|
import com.codemate.booklibrary.data.Book;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public interface MainView {
|
||||||
|
void showBooks(List<Book> books);
|
||||||
|
}
|
||||||
27
app/src/main/res/layout/activity_main.xml
Normal file
27
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
|
<android.support.v7.widget.SearchView
|
||||||
|
android:id="@+id/searchView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
app:iconifiedByDefault="false"
|
||||||
|
app:queryHint="Search by title, author or year..." />
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/bookRecycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:layout_below="@id/searchView"
|
||||||
|
android:scrollbars="vertical"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
30
app/src/main/res/layout/item_book.xml
Normal file
30
app/src/main/res/layout/item_book.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bookTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Title"
|
||||||
|
tools:text="Book Title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bookAuthor"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||||
|
tools:text="Book Author" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bookDate"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||||
|
tools:text="2016" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||||
|
(such as screen margins) for screens with more than 820dp of available width. This
|
||||||
|
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||||
|
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||||
|
</resources>
|
||||||
6
app/src/main/res/values/colors.xml
Normal file
6
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
</resources>
|
||||||
5
app/src/main/res/values/dimens.xml
Normal file
5
app/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
</resources>
|
||||||
3
app/src/main/res/values/strings.xml
Normal file
3
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Book Library</string>
|
||||||
|
</resources>
|
||||||
11
app/src/main/res/values/styles.xml
Normal file
11
app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.codemate.booklibrary;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import cucumber.api.CucumberOptions;
|
||||||
|
import cucumber.api.junit.Cucumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 01/09/16.
|
||||||
|
*/
|
||||||
|
@RunWith(Cucumber.class)
|
||||||
|
@CucumberOptions(features = "src/test/resources")
|
||||||
|
public class CucumberTests extends TestCase {
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.codemate.booklibrary.data;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public class LibraryTest {
|
||||||
|
@Test
|
||||||
|
public void addMultipleBooks_PersistsThemInLibrary() {
|
||||||
|
List<Book> books = RandomBookGenerator.randomBooks(3);
|
||||||
|
Library library = new Library();
|
||||||
|
library.addBooks(books);
|
||||||
|
|
||||||
|
Assert.assertEquals(books, library.getAllBooks());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.codemate.booklibrary.data;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public class RandomBookGeneratorTest {
|
||||||
|
@Test
|
||||||
|
public void generatingRandomBooks_ReturnsNonEmptyBookList() {
|
||||||
|
List<Book> books = RandomBookGenerator.randomBooks(15);
|
||||||
|
|
||||||
|
assertEquals(15, books.size());
|
||||||
|
|
||||||
|
for (Book book : books) {
|
||||||
|
assertNotNull(book.getTitle());
|
||||||
|
assertNotNull(book.getAuthor());
|
||||||
|
assertNotEquals(0, book.getPublishDate().getTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.codemate.booklibrary.steps;
|
||||||
|
|
||||||
|
import com.codemate.booklibrary.data.Book;
|
||||||
|
import com.codemate.booklibrary.data.Library;
|
||||||
|
import com.codemate.booklibrary.ui.MainPresenter;
|
||||||
|
import com.codemate.booklibrary.ui.MainView;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import cucumber.api.Format;
|
||||||
|
import cucumber.api.PendingException;
|
||||||
|
import cucumber.api.java.en.And;
|
||||||
|
import cucumber.api.java.en.Given;
|
||||||
|
import cucumber.api.java.en.Then;
|
||||||
|
import cucumber.api.java.en.When;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 01/09/16.
|
||||||
|
*/
|
||||||
|
public class BookSearchSteps {
|
||||||
|
private Library library = new Library();
|
||||||
|
private List<Book> results = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test data populations
|
||||||
|
*/
|
||||||
|
@Given(".*a book with title \"([^\"]*)\", written by \"([^\"]*)\", published in (.+)$")
|
||||||
|
public void addNewBook(String title, String author, @Format("dd MMMM yyyy") Date publishDate) throws Throwable {
|
||||||
|
Book book = new Book(title, author, publishDate);
|
||||||
|
library.addBook(book);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use cases
|
||||||
|
*/
|
||||||
|
@When("^the customer wants to know all books in the library$")
|
||||||
|
public void getAllBooks() throws Throwable {
|
||||||
|
results = library.getAllBooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^the customer searches for books by author \"([^\"]*)\"$")
|
||||||
|
public void searchByAuthor(String authorQuery) throws Throwable {
|
||||||
|
results = library.search(authorQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^the customer searches for books with title \"([^\"]*)\"$")
|
||||||
|
public void searchByTitle(String title) throws Throwable {
|
||||||
|
results = library.search(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^the customer searches for books published in year (.*)$")
|
||||||
|
public void searchByYear(String year) throws Throwable {
|
||||||
|
results = library.search(year);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assertions
|
||||||
|
*/
|
||||||
|
@Then("^(\\d+) books should be found$")
|
||||||
|
public void verifyAmountOfBooksFound(int booksFound) throws Throwable {
|
||||||
|
assertEquals(booksFound, results.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@And("^Book (\\d+) should have the title \"([^\"]*)\"$")
|
||||||
|
public void verifyBookAtPosition(int position, String title) throws Throwable {
|
||||||
|
int realPosition = position - 1;
|
||||||
|
Assert.assertEquals(title, results.get(realPosition).getTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
@And("^Books should be (.+)$")
|
||||||
|
public void verifyBookTitlesEqualTo(String titles) throws Throwable {
|
||||||
|
for (Book result : results) {
|
||||||
|
assertThat(titles, containsString(result.getTitle()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.codemate.booklibrary.ui;
|
||||||
|
|
||||||
|
import com.codemate.booklibrary.data.Book;
|
||||||
|
import com.codemate.booklibrary.data.Library;
|
||||||
|
import com.codemate.booklibrary.data.RandomBookGenerator;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by ironman on 02/09/16.
|
||||||
|
*/
|
||||||
|
public class MainPresenterTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MainView mainView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Library library;
|
||||||
|
|
||||||
|
private MainPresenter presenter;
|
||||||
|
private List<Book> randomBooks;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
presenter = new MainPresenter(mainView, library);
|
||||||
|
randomBooks = RandomBookGenerator.randomBooks(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchForBooks_ShowsThemInUI() {
|
||||||
|
when(library.search(anyString()))
|
||||||
|
.thenReturn(randomBooks);
|
||||||
|
presenter.searchForBooks("test_search");
|
||||||
|
|
||||||
|
verify(mainView).showBooks(randomBooks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadAllBooks_ShowsThemInUI() {
|
||||||
|
when(library.getAllBooks())
|
||||||
|
.thenReturn(randomBooks);
|
||||||
|
presenter.loadAllBooks();
|
||||||
|
|
||||||
|
verify(mainView).showBooks(randomBooks);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
Feature: Book Search
|
||||||
|
A customer must be able to search books by year, author or title
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the library has a book with title "How to be awesome", written by "Iiro Krankka", published in 16 May 2016
|
||||||
|
And a book with title "My life as an awesome guy", written by "Iiro Krankka", published in 27 July 2016
|
||||||
|
And a book with title "I think my teacher is an asshat", written by "John Doe", published in 01 January 2010
|
||||||
|
|
||||||
|
Scenario: List all books
|
||||||
|
When the customer wants to know all books in the library
|
||||||
|
Then 3 books should be found
|
||||||
|
And Book 1 should have the title "How to be awesome"
|
||||||
|
And Book 2 should have the title "My life as an awesome guy"
|
||||||
|
And Book 3 should have the title "I think my teacher is an asshat"
|
||||||
|
|
||||||
|
Scenario: Search books by year
|
||||||
|
When the customer searches for books published in year 2016
|
||||||
|
Then 2 books should be found
|
||||||
|
And Book 1 should have the title "How to be awesome"
|
||||||
|
And Book 2 should have the title "My life as an awesome guy"
|
||||||
|
|
||||||
|
Scenario Outline: Search books by author
|
||||||
|
When the customer searches for books by author <author>
|
||||||
|
Then 2 books should be found
|
||||||
|
And Book 1 should have the title "How to be awesome"
|
||||||
|
And Book 2 should have the title "My life as an awesome guy"
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| author |
|
||||||
|
| "Iiro" |
|
||||||
|
| "Krankka" |
|
||||||
|
| "Iiro Krankka" |
|
||||||
|
|
||||||
|
Scenario Outline: Search books by title
|
||||||
|
When the customer searches for books with title <searched_title>
|
||||||
|
Then <search_results_count> books should be found
|
||||||
|
And Books should be <search_results>
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| searched_title | search_results_count | search_results |
|
||||||
|
| "Awesome" | 2 | "How to be awesome" and "My life as an awesome guy" |
|
||||||
|
| "asshat" | 1 | "I think my teacher is an asshat" |
|
||||||
|
| "How to be" | 1 | "How to be awesome" |
|
||||||
23
build.gradle
Normal file
23
build.gradle
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:2.1.3'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
18
gradle.properties
Normal file
18
gradle.properties
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||||
|
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#Thu Sep 01 16:32:45 EEST 2016
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
||||||
160
gradlew
vendored
Executable file
160
gradlew
vendored
Executable file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||||
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windowz variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
goto execute
|
||||||
|
|
||||||
|
:4NT_args
|
||||||
|
@rem Get arguments from the 4NT Shell from JP Software
|
||||||
|
set CMD_LINE_ARGS=%$
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
|||||||
|
include ':app'
|
||||||
Reference in New Issue
Block a user