Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement unexpectlengthexception in azure core #5108

Merged
merged 51 commits into from
Sep 5, 2019

Conversation

sima-zhu
Copy link
Contributor

Make changes in azure core. Added tests in storage.

Will make up more tests in azure core unit tests.

@sima-zhu sima-zhu added Storage Storage Service (Queues, Blobs, Files) Client This issue points to a problem in the data-plane of the library. labels Aug 26, 2019
@sima-zhu sima-zhu self-assigned this Aug 26, 2019
@sima-zhu sima-zhu requested a review from alzimmermsft August 27, 2019 00:57
@alzimmermsft
Copy link
Member

Could you turn on the unit tests in Blobs that use UnexpectedLengthException and make sure those pass with this change.


<testResources>
<testResource>
<directory>${basedir}/src/test/resources</directory>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this doing? I'm not super familiar with maven, but we don't have this in the blob pom even though we have the same folders in that project.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how groovy use the test resource folder. I did not see any file operation in blob. @alzimmermsft do you know how you do this in blob groovy test?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blobs doesn't use any file resources as part of testing, aside from the session-records which the playback framework handles the logic for that. Blobs generates and tears down file creation for the tests that need it.

@@ -18,12 +21,14 @@ import com.azure.storage.file.ShareClientBuilder
import com.azure.storage.file.models.ListSharesOptions
import spock.lang.Specification

import java.util.function.Supplier
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are all these imports added when all you did is remove a line?

@@ -195,6 +191,23 @@ class FileAPITests extends APISpec {
FileTestHelper.assertExceptionStatusCodeAndMessage(e, 416, StorageErrorCode.INVALID_RANGE)
}

@Unroll
def "Upload data length mismatch"() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can you put a blank line before each block label so it's easier to read?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! A little annoying if I have this in this PR. Will do in the issue.
#5066

where:
size | errMsg
6 | "more bytes than"
8 | "less bytes than"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: align.

@@ -141,6 +142,26 @@ class FileAsyncAPITests extends APISpec {
defaultData.clear()
}

@Unroll
def "Upload data length mismatch"() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as on the sync test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File downloadFile = new File(testFolder.getPath() + "/testDownload")
def testFolder = getClass().getClassLoader().getResource("testfiles")
File uploadFile = new File(testFolder.getPath(), "/helloworld")
File downloadFile = new File(testFolder.getPath(), "/testDownload")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Here you use "/helloworld" and in the sync tests you use "helloworld". They should both be the same but we may as well be consistent. Since the second argument is meant to be the file name, I'd say remove the slash here.

@@ -202,10 +204,15 @@ class FileTestHelper {

static String createRandomFileWithLength(int size, String folder, String fileName) {
def path = Paths.get(folder)
if (path == null) {
logger.logExceptionAsError("The folder path does not exist.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't work. The method accepts a RuntimeException and you pass it a string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed

@@ -202,10 +204,15 @@ class FileTestHelper {

static String createRandomFileWithLength(int size, String folder, String fileName) {
def path = Paths.get(folder)
if (path == null) {
logger.logExceptionAsError("The folder path does not exist.")
return null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we fail to make a file, I'd rather throw with a useful message instead of getting a null ref exception later in the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and throw

Copy link
Member

@jaschrep-msft jaschrep-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple reversions to make and a question that may involve moving some stuff.

@@ -325,6 +325,8 @@ class AppendBlobAPITest extends APISpec {
ByteArrayOutputStream downloadStream = new ByteArrayOutputStream(1024)
destURL.download(downloadStream)
downloadStream.toByteArray() == Arrays.copyOfRange(data, 2 * 1024, 3 * 1024)
cleanup:
defaultInputStream.get().reset()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultInputStream is a Supplier<InputStream> that news up a stream of the bytes of a preset string and just gives us that. These cleanup blocks are just getting a brand new stream only to call reset() and discard the reference for the GC to pick up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert it back.

@@ -50,7 +51,7 @@ class BlobAPITest extends APISpec {
HttpHeaders headers = response.headers()

then:
body == defaultData
defaultData.compareTo(body) == 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already idiomatic to groovy. == checks to see if Comparable is implemented and calls objA.compareTo(objB) == 0 if so. Otherwise, it calls objA.equals(objB).

(future reference: if you want to compare reference equality in groovy, you write objA.is(objB))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert the changes back. Thanks for the info!

* @throws UnexpectedLengthException when input data length mismatch input length.
* @throws RuntimeException When I/O error occurs.
*/
public static Flux<ByteBuffer> convertStreamToByteBuffer(InputStream data, long length, int blockSize) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is a common enough case that this should be in core? We can't be the only library moving data and need to do this conversion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will discuss with @JonathanGiles if we can put up the helper method in Azure core. Leave it here right now as only storage has this kind of needs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check the Utility methods. There are bunch a few which do not specific to storage. We can several of them into Azure core based on the needs of other clients. It is better to limit the scope of the utility to storage before preview 3.

Copy link
Member

@alzimmermsft alzimmermsft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just re-run the failed test step, MacOS has a transient test failure due to timing issues.

@@ -159,24 +161,14 @@ public AppendBlobItem appendBlock(InputStream data, long length) {
* @param context Additional context that is passed through the Http pipeline during the service call.
*
* @return A {@link Response} whose {@link Response#value() value} contains the append blob operation.
* @throws UnexpectedLengthException when the length of data does not match the input {@code length}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot throw UnexpectedLengthException here as it is not public API. You either need to make it public API, or not throw it from public API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put the exception in public exception folder


return Flux.defer(() -> {
Long expectedLength = Long.valueOf(request.headers().value("Content-Length"));
final long[] currentTotalLength = new long[1];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess what we're doing here is correct. I asked the question in SOF and got a response from David (the main contributor of rx) - https://stackoverflow.com/questions/57776012/mutating-array-elements-in-reactive-publisher-onnext-doonnext-methods/
The last thing to rule out is whether to use volatile keyword to ensure immediate update visibility across thread, waiting for his response.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Anu for taking care of this! Learned a lot from the thread.

return ByteBuffer.wrap(cache);
}));
Objects.requireNonNull(body);
long length = pageRange.end() - pageRange.start() + 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: final modifier for unmodifiable variable.

@sima-zhu sima-zhu merged commit f440dd6 into Azure:master Sep 5, 2019
@sima-zhu sima-zhu deleted the unexpectedException branch February 23, 2021 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Client This issue points to a problem in the data-plane of the library. Storage Storage Service (Queues, Blobs, Files)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants