Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
appinventor-sources
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
xpstem
appinventor-sources
Commits
38a69a94
Commit
38a69a94
authored
Dec 04, 2018
by
Evan W. Patton
Committed by
Susan Rati Lane
Dec 07, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix processing of // in File and add unit tests
Fixes #1457 Change-Id: I73ff292d87e8ba22a5900eac774d8ae576f05ef2
parent
b99cfc47
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
499 additions
and
14 deletions
+499
-14
appinventor/components/src/com/google/appinventor/components/runtime/Form.java
...s/src/com/google/appinventor/components/runtime/Form.java
+13
-9
appinventor/components/src/com/google/appinventor/components/runtime/ReplForm.java
...c/com/google/appinventor/components/runtime/ReplForm.java
+2
-2
appinventor/components/tests/assets/com.google.appinventor.components.runtime.test/test.txt
...s/com.google.appinventor.components.runtime.test/test.txt
+1
-0
appinventor/components/tests/assets/test.txt
appinventor/components/tests/assets/test.txt
+1
-0
appinventor/components/tests/com/google/appinventor/components/runtime/FileTest.java
...s/com/google/appinventor/components/runtime/FileTest.java
+160
-0
appinventor/components/tests/com/google/appinventor/components/runtime/FormTest.java
...s/com/google/appinventor/components/runtime/FormTest.java
+79
-0
appinventor/components/tests/com/google/appinventor/components/runtime/ReplFormTest.java
...m/google/appinventor/components/runtime/ReplFormTest.java
+97
-0
appinventor/components/tests/com/google/appinventor/components/runtime/RobolectricTestBase.java
...e/appinventor/components/runtime/RobolectricTestBase.java
+21
-2
appinventor/components/tests/com/google/appinventor/components/runtime/repltest/FileReplTest.java
...appinventor/components/runtime/repltest/FileReplTest.java
+26
-0
appinventor/components/tests/com/google/appinventor/components/runtime/shadows/ShadowActivityCompat.java
...ntor/components/runtime/shadows/ShadowActivityCompat.java
+56
-0
appinventor/components/tests/com/google/appinventor/components/runtime/shadows/ShadowEventDispatcher.java
...tor/components/runtime/shadows/ShadowEventDispatcher.java
+20
-1
appinventor/components/tests/com/google/appinventor/components/runtime/test/TestExtension.java
...le/appinventor/components/runtime/test/TestExtension.java
+23
-0
No files found.
appinventor/components/src/com/google/appinventor/components/runtime/Form.java
View file @
38a69a94
...
...
@@ -25,6 +25,7 @@ import android.os.AsyncTask;
import
android.os.Build
;
import
android.os.Bundle
;
import
android.os.Handler
;
import
android.support.annotation.VisibleForTesting
;
import
android.support.v4.app.ActivityCompat
;
import
android.support.v4.content.ContextCompat
;
import
android.util.Log
;
...
...
@@ -74,6 +75,7 @@ import com.google.appinventor.components.runtime.util.SdkLevel;
import
com.google.appinventor.components.runtime.util.ViewUtil
;
import
org.json.JSONException
;
import
java.io.File
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.io.InputStream
;
...
...
@@ -2530,13 +2532,7 @@ public class Form extends AppInventorCompatActivity
*/
@SuppressWarnings
({
"WeakerAccess"
})
// May be called by extensions
public
InputStream
openAsset
(
String
asset
)
throws
IOException
{
String
path
=
getAssetPath
(
asset
);
if
(
path
.
startsWith
(
ASSETS_PREFIX
))
{
final
AssetManager
am
=
getAssets
();
return
am
.
open
(
path
.
substring
(
ASSETS_PREFIX
.
length
()));
}
else
{
return
FileUtil
.
openFile
(
URI
.
create
(
path
));
}
return
openAssetInternal
(
getAssetPath
(
asset
));
}
/**
...
...
@@ -2563,13 +2559,21 @@ public class Form extends AppInventorCompatActivity
* stream to prevent resource leaking.
* @throws IOException if the asset is not found or cannot be read
*/
@SuppressWarnings
(
"unused"
)
// May be called by extensions
public
InputStream
openAssetForExtension
(
Component
component
,
String
asset
)
throws
IOException
{
String
path
=
getAssetPathForExtension
(
component
,
asset
);
return
openAssetInternal
(
getAssetPathForExtension
(
component
,
asset
));
}
@SuppressWarnings
(
"WeakerAccess"
)
// Visible for testing
@VisibleForTesting
InputStream
openAssetInternal
(
String
path
)
throws
IOException
{
if
(
path
.
startsWith
(
ASSETS_PREFIX
))
{
final
AssetManager
am
=
getAssets
();
return
am
.
open
(
path
.
substring
(
ASSETS_PREFIX
.
length
()));
}
else
{
}
else
if
(
path
.
startsWith
(
"file:"
))
{
return
FileUtil
.
openFile
(
URI
.
create
(
path
));
}
else
{
return
FileUtil
.
openFile
(
path
);
}
}
}
appinventor/components/src/com/google/appinventor/components/runtime/ReplForm.java
View file @
38a69a94
...
...
@@ -62,7 +62,7 @@ public class ReplForm extends Form {
private
static
final
String
LOG_TAG
=
ReplForm
.
class
.
getSimpleName
();
private
AppInvHTTPD
httpdServer
=
null
;
public
static
ReplForm
topform
;
p
rivate
static
final
String
REPL_ASSET_DIR
=
p
ublic
static
final
String
REPL_ASSET_DIR
=
Environment
.
getExternalStorageDirectory
().
getAbsolutePath
()
+
"/AppInventor/assets/"
;
private
static
final
String
REPL_COMP_DIR
=
REPL_ASSET_DIR
+
"external_comps/"
;
...
...
@@ -402,7 +402,7 @@ public class ReplForm extends Form {
@Override
public
String
getAssetPath
(
String
asset
)
{
return
REPL_ASSET_DIR
+
asset
;
return
"file://"
+
REPL_ASSET_DIR
+
asset
;
}
@Override
...
...
appinventor/components/tests/assets/com.google.appinventor.components.runtime.test/test.txt
0 → 100644
View file @
38a69a94
Sample extension asset
appinventor/components/tests/assets/test.txt
0 → 100644
View file @
38a69a94
Hello, world!
appinventor/components/tests/com/google/appinventor/components/runtime/FileTest.java
0 → 100644
View file @
38a69a94
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright © 2018 Massachusetts Institute of Technology, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package
com.google.appinventor.components.runtime
;
import
android.Manifest
;
import
android.os.Environment
;
import
com.google.appinventor.components.runtime.shadows.ShadowActivityCompat
;
import
com.google.appinventor.components.runtime.shadows.ShadowAsynchUtil
;
import
com.google.appinventor.components.runtime.shadows.ShadowEventDispatcher
;
import
com.google.appinventor.components.runtime.util.IOUtils
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.robolectric.Shadows
;
import
org.robolectric.annotation.Config
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.nio.charset.Charset
;
/**
* Tests for the File component.
*
* @author ewpatton@mit.edu (Evan W. Patton)
*/
@Config
(
shadows
=
{
ShadowActivityCompat
.
class
})
public
class
FileTest
extends
RobolectricTestBase
{
private
static
final
String
TAG
=
FileTest
.
class
.
getSimpleName
();
private
static
final
String
DATA
=
"test data"
;
protected
static
final
String
TARGET_FILE
=
"test.txt"
;
protected
File
file
;
@Before
public
void
setUp
()
{
super
.
setUp
();
file
=
new
File
(
getForm
());
}
/**
* Tests that the File component can read files when given the special file
* path "//". In a compiled app, this reads from the app's compiled assets.
* For the REPL, this reads from a special directory AppInventor/assets/
* on the external storage.
*/
@Test
public
void
testFileDoubleSlash
()
{
grantFilePermissions
();
file
.
ReadFrom
(
"//"
+
TARGET_FILE
);
ShadowAsynchUtil
.
runAllPendingRunnables
();
runAllEvents
();
ShadowEventDispatcher
.
assertEventFired
(
file
,
"GotText"
,
"Hello, world!\n"
);
}
/**
* Tests that the File component can read files when given a relative file
* name. In a compiled app, this reads from the app's private data directory.
* For the REPL, this reads from a special directory AppInventor/data/
* on the external storage.
*/
@Test
public
void
testFileRelative
()
{
grantFilePermissions
();
writeTempFile
(
TARGET_FILE
,
DATA
,
false
);
testReadFile
(
TARGET_FILE
,
DATA
);
}
/**
* Tests that the File component can read files when given an absolute file
* name. In both compiled apps and the REPL, this reads from the given file
* from the root of the external storage.
*/
@Test
public
void
testFileAbsolute
()
{
grantFilePermissions
();
writeTempFile
(
TARGET_FILE
,
DATA
,
true
);
testReadFile
(
"/"
+
TARGET_FILE
,
DATA
);
}
/**
* Tests that the file component will report a PermissionDenied event when
* the user denies a request for READ_EXTERNAL_STORAGE.
*/
@Test
public
void
testFilePermissionDenied
()
{
denyFilePermissions
();
file
.
ReadFrom
(
"/"
+
TARGET_FILE
);
ShadowActivityCompat
.
denyLastRequestedPermissions
();
runAllEvents
();
ShadowEventDispatcher
.
assertPermissionDenied
(
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
);
}
/// Helper functions
/**
* Helper function to grant read/write permissions to the app.
*/
public
void
grantFilePermissions
()
{
Shadows
.
shadowOf
(
getForm
()).
grantPermissions
(
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
);
Shadows
.
shadowOf
(
getForm
()).
grantPermissions
(
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
);
}
/**
* Helper function to deny read/write permissions to the app.
*/
public
void
denyFilePermissions
()
{
Shadows
.
shadowOf
(
getForm
()).
denyPermissions
(
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
);
Shadows
.
shadowOf
(
getForm
()).
denyPermissions
(
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
);
}
/**
* Write a temporary file to the file of the given {@code name} with the given
* {@code content}.
*
* @param name the name of the file to write
* @param content the content of the file
* @param external true if the file should be written to external storage,
* otherwise false.
* @return the absolute path of the file
*/
public
String
writeTempFile
(
String
name
,
String
content
,
boolean
external
)
{
String
target
;
if
(
external
)
{
target
=
Environment
.
getExternalStorageDirectory
().
getAbsolutePath
();
}
else
if
(
getForm
().
isRepl
())
{
target
=
Environment
.
getExternalStorageDirectory
().
getAbsolutePath
()
+
"/AppInventor/data"
;
}
else
{
target
=
getForm
().
getFilesDir
().
getAbsolutePath
();
}
target
+=
"/"
+
name
;
FileOutputStream
out
=
null
;
try
{
java
.
io
.
File
targetFile
=
new
java
.
io
.
File
(
target
);
targetFile
.
deleteOnExit
();
if
(!
targetFile
.
getParentFile
().
exists
())
{
if
(!
targetFile
.
getParentFile
().
mkdirs
())
{
throw
new
IOException
();
}
}
out
=
new
FileOutputStream
(
target
);
out
.
write
(
content
.
getBytes
(
Charset
.
forName
(
"UTF-8"
)));
return
targetFile
.
getAbsolutePath
();
}
catch
(
IOException
e
)
{
throw
new
IllegalStateException
(
"Unable to prepare test"
,
e
);
}
finally
{
IOUtils
.
closeQuietly
(
TAG
,
out
);
}
}
private
void
testReadFile
(
String
filename
,
String
expectedData
)
{
file
.
ReadFrom
(
filename
);
ShadowAsynchUtil
.
runAllPendingRunnables
();
runAllEvents
();
ShadowEventDispatcher
.
assertEventFired
(
file
,
"GotText"
,
expectedData
);
}
}
appinventor/components/tests/com/google/appinventor/components/runtime/FormTest.java
0 → 100644
View file @
38a69a94
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright © 2018 Massachusetts Institute of Technology, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package
com.google.appinventor.components.runtime
;
import
com.google.appinventor.components.runtime.test.TestExtension
;
import
com.google.appinventor.components.runtime.util.IOUtils
;
import
org.junit.Test
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.nio.charset.Charset
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
/**
* Tests for the Form component.
*
* @author ewpatton@mit.edu (Evan W. Patton)
*/
public
class
FormTest
extends
RobolectricTestBase
{
private
static
final
int
BUFSIZE
=
4096
;
static
final
String
TARGET_FILE
=
"test.txt"
;
/**
* Tests the ability of the Form to open an asset.
*
* @throws IOException if the asset cannot be found.
*/
@Test
public
void
testOpenAsset
()
throws
IOException
{
InputStream
is
=
null
;
try
{
is
=
getForm
().
openAsset
(
TARGET_FILE
);
assertEquals
(
"Hello, world!\n"
,
readStream
(
is
));
}
finally
{
IOUtils
.
closeQuietly
(
"test"
,
is
);
}
}
/**
* Tests the ability of the Form to open an asset associated with an
* extension.
*
* @throws IOException if the asset cannot be found.
*/
@Test
public
void
testOpenAssetExtension
()
throws
IOException
{
InputStream
is
=
null
;
try
{
is
=
getForm
().
openAssetForExtension
(
new
TestExtension
(
getForm
()),
TARGET_FILE
);
assertEquals
(
"Sample extension asset\n"
,
readStream
(
is
));
}
finally
{
IOUtils
.
closeQuietly
(
"test"
,
is
);
}
}
/// Helper functions
/**
* Read the contents of a stream as a string.
*
* @param is the input stream to read
* @return the contents of the input stream as a string
* @throws IOException if the file cannot be read
*/
public
static
String
readStream
(
InputStream
is
)
throws
IOException
{
byte
[]
data
=
new
byte
[
BUFSIZE
];
int
read
;
StringBuilder
sb
=
new
StringBuilder
();
while
((
read
=
is
.
read
(
data
,
0
,
BUFSIZE
))
>
0
)
{
sb
.
append
(
new
String
(
data
,
0
,
read
,
Charset
.
forName
(
"UTF-8"
)));
}
return
sb
.
toString
();
}
}
appinventor/components/tests/com/google/appinventor/components/runtime/ReplFormTest.java
0 → 100644
View file @
38a69a94
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright © 2018 Massachusetts Institute of Technology, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package
com.google.appinventor.components.runtime
;
import
android.Manifest
;
import
com.google.appinventor.components.runtime.util.IOUtils
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.robolectric.Shadows
;
import
org.robolectric.shadows.ShadowApplication
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
/**
* Tests for the ReplForm.
*
* @author ewpatton@mit.edu (Evan W. Patton)
*/
public
class
ReplFormTest
extends
FormTest
{
private
static
final
String
TAG
=
"test"
;
private
static
final
int
BUFSIZE
=
4096
;
@Before
public
void
setUp
()
{
super
.
setUpAsRepl
();
Shadows
.
shadowOf
(
getForm
()).
grantPermissions
(
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
);
Shadows
.
shadowOf
(
getForm
()).
grantPermissions
(
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
);
copyAssetToReplAssets
(
TARGET_FILE
,
TARGET_FILE
);
copyAssetToReplAssets
(
"com.google.appinventor.components.runtime.test/"
+
TARGET_FILE
,
"external_comps/com.google.appinventor.components.runtime.test/assets/"
+
TARGET_FILE
);
}
/**
* Test that the form can open naked file paths.
*
* In version 2.48, ReplForm did not follow the contract for {@link Form#getAssetPath(String)}.
* It returned a string containing a file path, not a file URI. This would result in developers
* getting an error "URI is not absolute" when live testing. We changed the ReplForm
* implementation to behave correctly by returning a string starting with file:///, but we also
* test here that we can handle non-URI versions in case someone ends up overriding the method in
* a subclass (somehow...).
*/
@Test
public
void
testOpenAssetInternal
()
throws
IOException
{
InputStream
is
=
null
;
try
{
is
=
getForm
().
openAssetInternal
(
ReplForm
.
REPL_ASSET_DIR
+
TARGET_FILE
);
assertEquals
(
"Hello, world!\n"
,
readStream
(
is
));
}
finally
{
IOUtils
.
closeQuietly
(
TAG
,
is
);
}
}
/// Helper functions
/**
* Copy an asset from the given source name to the given target name in the
* /sdcard/AppInventor/assets directory.
*
* @param source the source asset path
* @param target the target asset path
*/
public
static
void
copyAssetToReplAssets
(
String
source
,
String
target
)
{
InputStream
in
=
null
;
OutputStream
out
=
null
;
try
{
in
=
ShadowApplication
.
getInstance
().
getApplicationContext
().
getAssets
().
open
(
source
);
java
.
io
.
File
targetFile
=
new
java
.
io
.
File
(
ReplForm
.
REPL_ASSET_DIR
+
target
);
if
(!
targetFile
.
getParentFile
().
exists
())
{
if
(!
targetFile
.
getParentFile
().
mkdirs
())
{
throw
new
IllegalStateException
(
"Could not configure REPL assets in setup"
);
}
}
out
=
new
FileOutputStream
(
targetFile
);
byte
[]
buffer
=
new
byte
[
BUFSIZE
];
int
bytesRead
;
while
((
bytesRead
=
in
.
read
(
buffer
,
0
,
BUFSIZE
))
>
0
)
{
out
.
write
(
buffer
,
0
,
bytesRead
);
}
}
catch
(
IOException
e
)
{
throw
new
IllegalStateException
(
"Could not configure REPL assets in test setup"
,
e
);
}
finally
{
IOUtils
.
closeQuietly
(
TAG
,
in
);
IOUtils
.
closeQuietly
(
TAG
,
out
);
}
}
}
appinventor/components/tests/com/google/appinventor/components/runtime/RobolectricTestBase.java
View file @
38a69a94
...
...
@@ -28,7 +28,7 @@ import java.util.concurrent.TimeUnit;
* @author ewpatton@mit.edu (Evan W. Patton)
*/
@RunWith
(
RobolectricTestRunner
.
class
)
@Config
(
sdk
=
2
2
,
manifest
=
"tests/AndroidManifest.xml"
,
@Config
(
sdk
=
2
3
,
manifest
=
"tests/AndroidManifest.xml"
,
shadows
=
{
ShadowStorageUtils
.
class
,
ShadowEventDispatcher
.
class
,
ShadowAsynchUtil
.
class
})
public
class
RobolectricTestBase
{
...
...
@@ -45,14 +45,33 @@ public class RobolectricTestBase {
}
}
private
static
class
FakeReplForm
extends
ReplForm
{
@Override
protected
void
$define
()
{}
@Override
public
void
dispatchErrorOccurredEvent
(
Component
component
,
String
functionName
,
int
errorCode
,
Object
...
args
)
{
String
message
=
ErrorMessages
.
formatMessage
(
errorCode
,
args
);
ShadowEventDispatcher
.
dispatchEvent
(
this
.
$form
(),
"ErrorOccurred"
,
component
,
functionName
,
errorCode
,
message
);
}
}
public
Form
getForm
()
{
return
form
;
}
@Before
public
void
setUp
()
{
setUpForm
(
FakeForm
.
class
);
}
public
void
setUpAsRepl
()
{
setUpForm
(
FakeReplForm
.
class
);
}
private
<
T
extends
Form
>
void
setUpForm
(
Class
<
T
>
clazz
)
{
ShadowLooper
.
getShadowMainLooper
().
getScheduler
().
setIdleState
(
IdleState
.
PAUSED
);
ActivityController
<
FakeForm
>
activityController
=
Robolectric
.
buildActivity
(
FakeForm
.
class
)
ActivityController
<
T
>
activityController
=
Robolectric
.
buildActivity
(
clazz
)
.
create
().
start
().
resume
().
visible
();
form
=
activityController
.
get
();
// Unfortunately Robolectric won't handle laying out the view hierarchy and because of how
...
...
appinventor/components/tests/com/google/appinventor/components/runtime/repltest/FileReplTest.java
0 → 100644
View file @
38a69a94
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright © 2018 Massachusetts Institute of Technology, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package
com.google.appinventor.components.runtime.repltest
;
import
com.google.appinventor.components.runtime.File
;
import
com.google.appinventor.components.runtime.FileTest
;
import
com.google.appinventor.components.runtime.ReplFormTest
;
import
org.junit.Before
;
/**
* Tests for the File Component, but run as if it is in the REPL rather than
* in a compiled application. See {@link FileTest} for the test definitions.
*
* @author ewpatton@mit.edu (Evan W. Patton)
*/
public
class
FileReplTest
extends
FileTest
{
@Before
public
void
setUp
()
{
setUpAsRepl
();
file
=
new
File
(
getForm
());
ReplFormTest
.
copyAssetToReplAssets
(
TARGET_FILE
,
TARGET_FILE
);
}
}
appinventor/components/tests/com/google/appinventor/components/runtime/shadows/ShadowActivityCompat.java
0 → 100644
View file @
38a69a94
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright © 2018 Massachusetts Institute of Technology, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package
com.google.appinventor.components.runtime.shadows
;
import
android.app.Activity
;
import
android.content.pm.PackageManager
;
import
android.support.v4.app.ActivityCompat
;
import
org.robolectric.annotation.Implementation
;
import
org.robolectric.annotation.Implements
;
/**
* A shadow implementation of ActivityCompat that allows one to
* grant/deny permissions requested when the app calls
* {@link ActivityCompat#requestPermission(Activity,String,String)}.
*
* @author ewpatton@mit.edu (Evan W. Patton)
*/
@Implements
(
ActivityCompat
.
class
)
public
class
ShadowActivityCompat
{
private
static
Activity
activity
;
private
static
String
[]
permissions
;
private
static
int
requestCode
;
@Implementation
public
static
void
requestPermissions
(
final
Activity
activity
,
final
String
[]
permissions
,
final
int
requestCode
)
{
ShadowActivityCompat
.
activity
=
activity
;
ShadowActivityCompat
.
permissions
=
permissions
;
ShadowActivityCompat
.
requestCode
=
requestCode
;
}
/**
* Grants the last set of permissions requested by the app.
*/
public
static
void
grantLastRequestedPermissions
()
{
int
[]
result
=
new
int
[
permissions
.
length
];
for
(
int
i
=
0
;
i
<
result
.
length
;
i
++)
{
result
[
i
]
=
PackageManager
.
PERMISSION_GRANTED
;
}
activity
.
onRequestPermissionsResult
(
requestCode
,
permissions
,
result
);
}
/**
* Denies the last set of permissions requested by the app.
*/
public
static
void
denyLastRequestedPermissions
()
{
int
[]
result
=
new
int
[
permissions
.
length
];
for
(
int
i
=
0
;
i
<
result
.
length
;
i
++)
{
result
[
i
]
=
PackageManager
.
PERMISSION_DENIED
;
}
activity
.
onRequestPermissionsResult
(
requestCode
,
permissions
,
result
);
}
}
appinventor/components/tests/com/google/appinventor/components/runtime/shadows/ShadowEventDispatcher.java
View file @
38a69a94
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright © 2017 Massachusetts Institute of Technology, All rights reserved.
// Copyright © 2017
-2018
Massachusetts Institute of Technology, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
...
...
@@ -111,4 +111,23 @@ public class ShadowEventDispatcher {
}
throw
new
AssertionError
(
String
.
format
(
"Form did not receive ErrorOccurred event with code %d."
,
errorCode
));
}
/**
* Asserts that the EventDispatcher saw a PermissionDenied event for the given permission name.
*
* @param permission the permission to test for denial
*/
public
static
void
assertPermissionDenied
(
String
permission
)
{
if
(
permission
.
startsWith
(
"android.permission."
))
{
permission
=
permission
.
replace
(
"android.permission."
,
""
);
}
for
(
Set
<
EventWithArgs
>
events:
firedEvents
.
values
())
{
for
(
EventWithArgs
event
:
events
)
{
if
(
"PermissionDenied"
.
equals
(
event
.
eventName
)
&&
event
.
args
[
2
].
equals
(
permission
))
{
return
;
}
}
}
throw
new
AssertionError
(
String
.
format
(
"Form did not receive PermissionDenied event for permission %s."
,
permission
));
}
}
appinventor/components/tests/com/google/appinventor/components/runtime/test/TestExtension.java
0 → 100644
View file @
38a69a94
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright © 2018 Massachusetts Institute of Technology, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package
com.google.appinventor.components.runtime.test
;
import
com.google.appinventor.components.annotations.SimpleObject
;
import
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
;
import
com.google.appinventor.components.runtime.Form
;
/**
* TestExtension is a barebones extension class that can be used for testing
* different features of the App Inventor extensions mechanism in the REPL.
*
* @author ewpatton@mit.edu (Evan W. Patton)
*/
@SimpleObject
(
external
=
true
)
public
class
TestExtension
extends
AndroidNonvisibleComponent
{
public
TestExtension
(
Form
form
)
{
super
(
form
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment