Skip to content

Commit 52a1464

Browse files
Myusaktiwanari
authored andcommitted
Refactor Blocks (#35)
* Refactor Blocks Following changes are made in this commit: * Rename classes As we discussed offline * Extract common functions among block classes Changed NumberTextHolder interface for better encapsulation * Change return value type of BlockBase#getKind The previous code uses Class class, which does not represent actual use case (e.g., ForwardSecBlock#getKind returns SequenceBlock.class). Replaced Class<? extends Block> type with Enum. * Remove locale-dependent code on parsing numbers The previous code relies on the Locale.US no matter what locale this app runs on. This commit fixes the locale dependent code by extracting the parsing logic. * Fix the unspoken rule that using scaled values for communicating between blocks and number pickers. The previous code scaled values so that the value has no radix point, which was confusing code. Fixed by replacing the type with BigDecimal and explicitly use the raw value. In accordance with it, changed the type of "number" in ProgramDataSpec for saving without data loss * Remove code in Unit class for handling quantities, which was ugly and cannot be used widely. Changed second string ("second/s" -> just "s") * Fix PR according to the comments on #35 * remove UNDO in BlockFactory.java * move FOREVER_WHILE_OFFSET in RepetitionBlock.java to ExecutionCondition.java * add Javadocs on NumberTextDelegate * change the signature of BlockSpaceManagerBase#placeBlock (ArrayList to List) * rename OnTouchNumTextListener to OnTouchNumberTextListener * remove OnNumberTextListener#context and rename holder to mHolder * rename Unit#getUnitString to decorateValue and introduce new class NumberUtil * Add and fix tests for the following classes: * NumberTextViewDelegate * Unit * NumberUtil * ParseUtil And cradle.build in the root directory is modified to add the dexmaker libraries which are necessary for mockito * Remove superfluous line-breaks * Add constructor guards for util classes
1 parent 09d6f07 commit 52a1464

57 files changed

Lines changed: 1445 additions & 948 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ dependencies {
8080
androidTestCompile 'com.android.support:support-annotations:23.4.0'
8181

8282
// mockito
83-
androidTestCompile 'org.mockito:mockito-core:1.9.5'
83+
androidTestCompile 'org.mockito:mockito-core:1.10.19'
84+
androidTestCompile "com.crittercism.dexmaker:dexmaker:1.4"
85+
androidTestCompile "com.crittercism.dexmaker:dexmaker-dx:1.4"
86+
androidTestCompile "com.crittercism.dexmaker:dexmaker-mockito:1.4"
8487

8588
// our library
8689
compile 'com.pileproject:drivecommand:2.1.0'
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (C) 2011-2015 PILE Project, Inc. <dev@pileproject.com>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.pileproject.drive.programming.visual.block;
18+
19+
import android.support.test.runner.AndroidJUnit4;
20+
import android.widget.TextView;
21+
22+
import org.junit.Before;
23+
import org.junit.Test;
24+
import org.junit.runner.RunWith;
25+
import org.mockito.Mockito;
26+
27+
import java.math.BigDecimal;
28+
29+
import static org.junit.Assert.*;
30+
import static org.mockito.Mockito.doReturn;
31+
import static org.mockito.Mockito.verify;
32+
33+
@RunWith(AndroidJUnit4.class)
34+
public class NumberTextViewDelegateTest {
35+
36+
private static final int PRECISION = 3;
37+
38+
public TextView mockTextView;
39+
40+
public NumberTextHolder mockNumberTextHolder;
41+
42+
public NumberTextViewDelegate numberTextViewDelegate;
43+
44+
@Before
45+
public void before() {
46+
mockTextView = Mockito.mock(TextView.class);
47+
48+
mockNumberTextHolder = Mockito.mock(NumberTextHolder.class);
49+
50+
doReturn(PRECISION).when(mockNumberTextHolder).getPrecision();
51+
52+
numberTextViewDelegate = new NumberTextViewDelegate(mockTextView, mockNumberTextHolder);
53+
}
54+
55+
@Test
56+
public void whenTextViewReturnsNumber_thenGetValueSucceeds() throws Exception {
57+
58+
doReturn("2.500").when(mockTextView).getText();
59+
assertEquals(new BigDecimal("2.5"), numberTextViewDelegate.getValue());
60+
}
61+
62+
@Test
63+
public void whenTextViewReturnsNumber_thenGetActionValueSucceeds() throws Exception {
64+
65+
doReturn("2.500").when(mockTextView).getText();
66+
assertEquals(2500, numberTextViewDelegate.getActionValue());
67+
}
68+
69+
@Test
70+
public void whenTextViewDisabled_thenVerifyTheMethodsCalled() throws Exception {
71+
72+
numberTextViewDelegate.enableTextView(false);
73+
74+
verify(mockTextView).setFocusable(false);
75+
verify(mockTextView).setFocusableInTouchMode(false);
76+
verify(mockTextView).setEnabled(false);
77+
}
78+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (C) 2011-2015 PILE Project, Inc. <dev@pileproject.com>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.pileproject.drive.util.development;
18+
19+
import android.content.res.Configuration;
20+
import android.content.res.Resources;
21+
22+
import com.pileproject.drive.app.DriveApplication;
23+
24+
import org.junit.Before;
25+
import org.junit.Test;
26+
import org.junit.experimental.runners.Enclosed;
27+
import org.junit.runner.RunWith;
28+
29+
import java.util.Locale;
30+
31+
import static junit.framework.Assert.assertEquals;
32+
33+
@RunWith(Enclosed.class)
34+
public class UnitTest {
35+
36+
public static void setLocale(Locale locale) {
37+
38+
Resources res = DriveApplication.getContext().getResources();
39+
Configuration config = res.getConfiguration();
40+
config.locale = locale;
41+
res.updateConfiguration(config, res.getDisplayMetrics());
42+
}
43+
44+
public static class InUS {
45+
46+
@Before
47+
public void before() {
48+
setLocale(Locale.US);
49+
}
50+
51+
@Test
52+
public void whenUnitIsSecond_thenDecoratesAsSecondString() throws Exception {
53+
assertEquals("When value is 1.0 and unit is second",
54+
"1.000s", Unit.Second.decorateValue(Locale.ENGLISH, 1.0, 3));
55+
}
56+
57+
@Test
58+
public void whenUnitIsPercent_thenDecoratesAsPercentString() throws Exception {
59+
assertEquals("When value is 100 and unit is percentage",
60+
"100%", Unit.Percentage.decorateValue(Locale.ENGLISH, 100, 0));
61+
}
62+
63+
@Test
64+
public void whenUnitIsNumberOfTimes_thenDecoratesAsNumberOfTimesString() throws Exception {
65+
assertEquals("When the value 100 and unit is NumberOfTimes",
66+
"Repeats: 100", Unit.NumberOfTimes.decorateValue(Locale.ENGLISH, 100, 0));
67+
}
68+
}
69+
70+
public static class InJP {
71+
72+
@Before
73+
public void before() {
74+
setLocale(Locale.JAPAN);
75+
}
76+
77+
@Test
78+
public void whenUnitIsSecond_thenDecoratesAsSecondString() throws Exception {
79+
assertEquals("When value is 1.0 and unit is second",
80+
"1.000びょう", Unit.Second.decorateValue(Locale.JAPAN, 1.0, 3));
81+
}
82+
83+
@Test
84+
public void whenUnitIsPercent_thenDecoratesAsPercentString() throws Exception {
85+
assertEquals("When value is 100 and unit is percentage",
86+
"100%", Unit.Percentage.decorateValue(Locale.JAPAN, 100, 0));
87+
}
88+
89+
@Test
90+
public void whenUnitIsNumberOfTimes_thenDecoratesAsNumberOfTimesString() throws Exception {
91+
assertEquals("When the value 100 and unit is NumberOfTimes",
92+
"100 かい", Unit.NumberOfTimes.decorateValue(Locale.JAPAN, 100, 0));
93+
}
94+
}
95+
96+
}

app/src/main/java/com/pileproject/drive/database/ProgramDataManager.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import com.pileproject.drive.programming.visual.activity.BlockPositionComparator;
2323
import com.pileproject.drive.programming.visual.block.BlockBase;
2424
import com.pileproject.drive.programming.visual.block.BlockFactory;
25-
import com.pileproject.drive.programming.visual.block.NumTextHolder;
25+
import com.pileproject.drive.programming.visual.block.NumberTextHolder;
2626
import com.pileproject.drive.programming.visual.layout.BlockSpaceLayout;
2727
import com.yahoo.squidb.data.SquidCursor;
2828
import com.yahoo.squidb.sql.Query;
@@ -129,8 +129,8 @@ private boolean saveProgram(String programName, String programType, BlockSpaceLa
129129
.setLeft(b.getLeft())
130130
.setTop(b.getTop());
131131
// get the number of TextView if the block has one
132-
if (b instanceof NumTextHolder) {
133-
data.setNumber(((NumTextHolder) b).getNum());
132+
if (b instanceof NumberTextHolder) {
133+
data.setNumber(((NumberTextHolder) b).getValueAsString());
134134
}
135135

136136
// save the data
@@ -202,8 +202,8 @@ private ArrayList<BlockBase> loadBlocks(SquidCursor<ProgramData> c) {
202202
// set data's properties
203203
b.setLeft(data.getLeft());
204204
b.setTop(data.getTop());
205-
if (b instanceof NumTextHolder) {
206-
((NumTextHolder) b).setNum(data.getNumber());
205+
if (b instanceof NumberTextHolder) {
206+
((NumberTextHolder) b).setValueAsString(data.getNumber());
207207
}
208208
// add the block to a list
209209
blocks.add(b);

app/src/main/java/com/pileproject/drive/database/ProgramDataSpec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,5 @@ public class ProgramDataSpec {
4747

4848
// the number which a number holder block has
4949
@ColumnSpec(defaultValue = "0")
50-
int number;
50+
String number;
5151
}

app/src/main/java/com/pileproject/drive/execution/ExecutionCondition.java

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@
1616

1717
package com.pileproject.drive.execution;
1818

19-
2019
import com.pileproject.drive.programming.visual.block.BlockBase;
21-
import com.pileproject.drive.programming.visual.block.repetition.RepetitionEndBlock;
22-
import com.pileproject.drive.programming.visual.block.repetition.WhileForeverBlock;
2320

2421
import java.util.ArrayList;
2522
import java.util.Collections;
@@ -31,6 +28,8 @@
3128
* A container class that has the condition of program execution.
3229
*/
3330
public class ExecutionCondition {
31+
private static final int FOREVER_WHILE_OFFSET = -1000;
32+
3433
private Stack<Integer> mWhileStack;
3534
private Stack<SelectionResult> mIfStack;
3635
private int mBeginningOfCurrentLoop;
@@ -147,10 +146,29 @@ public int sizeOfSelectionResult() {
147146
* Push the index of the beginning block of the current loop to a stack.
148147
* @param index the index of the beginning block
149148
*/
150-
public void pushBeginningOfLoop(int index) {
149+
private void pushBeginningOfLoop(int index) {
151150
mWhileStack.push(index);
152151
mBeginningOfCurrentLoop = index >= 0 ?
153-
index : index - WhileForeverBlock.FOREVER_WHILE_OFFSET;
152+
index : index - FOREVER_WHILE_OFFSET;
153+
}
154+
155+
/**
156+
* Enters infinite loop.
157+
*/
158+
public void enterInfiniteLoop() {
159+
int index = getProgramCount();
160+
pushBeginningOfLoop(index + FOREVER_WHILE_OFFSET); // push with offset
161+
}
162+
163+
/**
164+
* Enters N-times loop.
165+
* @param count number of times this loop repeats.
166+
*/
167+
public void enterNTimesLoop(int count) {
168+
int index = getProgramCount();
169+
for (int i = 1; i < count; i++) {
170+
pushBeginningOfLoop(index);
171+
}
154172
}
155173

156174
/**
@@ -160,7 +178,7 @@ public void reachEndOfLoop() {
160178
if (mWhileStack.isEmpty()) return ;
161179

162180
int index = mWhileStack.peek() >= 0 ?
163-
mWhileStack.peek() : mWhileStack.peek() - WhileForeverBlock.FOREVER_WHILE_OFFSET;
181+
mWhileStack.peek() : mWhileStack.peek() - FOREVER_WHILE_OFFSET;
164182

165183
// the loop has already finished
166184
if (mBeginningOfCurrentLoop != index) {
@@ -189,7 +207,7 @@ public void breakLoop() {
189207
// update index
190208
if (!mWhileStack.isEmpty()) {
191209
mBeginningOfCurrentLoop = mWhileStack.peek() >= 0 ?
192-
mWhileStack.peek() : mWhileStack.peek() - WhileForeverBlock.FOREVER_WHILE_OFFSET;
210+
mWhileStack.peek() : mWhileStack.peek() - FOREVER_WHILE_OFFSET;
193211
} else {
194212
mBeginningOfCurrentLoop = -1;
195213
}
@@ -199,7 +217,7 @@ public void breakLoop() {
199217

200218
// move to the end of the current loop
201219
for (; mBlocks.size() >= mProgramCount; ++mProgramCount) {
202-
if (mBlocks.get(mProgramCount).getKind() == RepetitionEndBlock.class) break;
220+
if (mBlocks.get(mProgramCount).getKind() == BlockBase.BlockKind.REPETITION_END) break;
203221
}
204222
}
205223
}

app/src/main/java/com/pileproject/drive/execution/ExecutionThread.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.pileproject.drive.app.DriveApplication;
2626
import com.pileproject.drive.database.ProgramDataManager;
2727
import com.pileproject.drive.programming.visual.block.BlockBase;
28+
import com.pileproject.drive.programming.visual.block.selection.SelectionEndBlock;
2829

2930
/**
3031
* A Thread class to execute program.

app/src/main/java/com/pileproject/drive/programming/visual/activity/ProgrammingActivityBase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
148148
// Get results
149149
int howToMake = data.getIntExtra(getString(R.string.key_block_how_to_make), BlockFactory.SEQUENCE);
150150
String blockName = data.getStringExtra(getString(R.string.key_block_block_name));
151-
ArrayList<BlockBase> blocks = BlockFactory.createBlocks(howToMake, blockName);
151+
List<BlockBase> blocks = BlockFactory.createBlocks(howToMake, blockName);
152152
mSpaceManager.addBlocks(blocks);
153153
}
154154
break;

app/src/main/java/com/pileproject/drive/programming/visual/block/BlockBase.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package com.pileproject.drive.programming.visual.block;
1818

1919
import android.content.Context;
20+
import android.support.annotation.LayoutRes;
21+
import android.view.LayoutInflater;
2022
import android.widget.RelativeLayout;
2123

2224
import com.pileproject.drive.execution.ExecutionCondition;
@@ -29,10 +31,20 @@
2931
* @version 1.0 18-June-2013
3032
*/
3133
public abstract class BlockBase extends RelativeLayout {
32-
public BlockBase(Context context) {
34+
35+
public BlockBase(Context context, @LayoutRes int layoutRes) {
3336
super(context);
37+
38+
LayoutInflater.from(context).inflate(layoutRes, this);
3439
}
3540

41+
/**
42+
* Returns kind of this block.
43+
*
44+
* @return {@link BlockKind}
45+
*/
46+
public abstract BlockKind getKind();
47+
3648
/**
3749
* Action that this block does while the execution of program.
3850
* Return delay that occurs after this action
@@ -45,10 +57,7 @@ public BlockBase(Context context) {
4557
public abstract int action(
4658
MachineController controller, ExecutionCondition condition);
4759

48-
/**
49-
* Return class to check a program is correct or not
50-
*
51-
* @return Class<? extends BaseBlock>
52-
*/
53-
public abstract Class<? extends BlockBase> getKind();
60+
public enum BlockKind {
61+
SEQUENCE, SELECTION_BEGIN, SELECTION_END, REPETITION_BEGIN, REPETITION_END, REPETITION_BREAK
62+
}
5463
}

0 commit comments

Comments
 (0)