Bug 1591857 [wpt PR 19942] - [WebNFC] Add tests for unformatted NDEF tag, a=testonly
authorWanming Lin <wanming.lin@intel.com>
Mon, 04 Nov 2019 11:11:14 +0000
changeset 564744 46eaf54b8e0fa2f1f46c4a7a44c5291abcb9ef3f
parent 564743 ba87a188bb896922597c21cfa10d01dda7ef19a6
child 564745 3ee76345e9a807f60c43c056b7098e9effa0a486
push id12351
push userffxbld-merge
push dateMon, 02 Dec 2019 11:32:26 +0000
treeherdermozilla-beta@dba4410526a2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1591857, 19942, 520391, 1012463, 1880537, 710249
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1591857 [wpt PR 19942] - [WebNFC] Add tests for unformatted NDEF tag, a=testonly Automatic update from web-platform-tests [WebNFC] Add tests for unformatted NDEF tag This CL adds a few tests to cover various checkpoints: - Read/write data from/to an unformatted NDEF tag, as well as interaction with NDEFPushOptions.overwrite attribute. - Read/write data from/to a non-NDEF tag, throws error. Relevant discussion in spec: https://github.com/w3c/web-nfc/issues/367 Bug: 520391, 1012463 Change-Id: I87cb7dc4611f8575b5616ecd98cc64249d79c4db Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1880537 Commit-Queue: Wanming Lin <wanming.lin@intel.com> Reviewed-by: Fran├žois Beaufort <beaufort.francois@gmail.com> Reviewed-by: Leon Han <leon.han@intel.com> Cr-Commit-Position: refs/heads/master@{#710249} -- wpt-commits: e44f20914735498a05fc835efe3e2cbde9f86f00 wpt-pr: 19942 Differential Revision: https://phabricator.services.mozilla.com/D53534
testing/web-platform/tests/resources/chromium/nfc-mock.js
testing/web-platform/tests/web-nfc/NDEFReader_scan.https.html
testing/web-platform/tests/web-nfc/NDEFWriter_push.https.html
testing/web-platform/tests/web-nfc/README.md
--- a/testing/web-platform/tests/resources/chromium/nfc-mock.js
+++ b/testing/web-platform/tests/resources/chromium/nfc-mock.js
@@ -27,17 +27,17 @@ function toMojoNDEFRecord(record) {
   let nfcRecord = new device.mojom.NDEFRecord();
   nfcRecord.recordType = record.recordType;
   nfcRecord.mediaType = record.mediaType;
   nfcRecord.data = toByteArray(record.data);
   return nfcRecord;
 }
 
 function toByteArray(data) {
-  // Convert JS objects to byte array.
+  // Converts JS objects to byte array.
   let byteArray = new Uint8Array(0);
   let tmpData = data;
 
   if (tmpData instanceof ArrayBuffer)
     byteArray = new Uint8Array(tmpData);
   else if (typeof tmpData === 'object' || typeof tmpData === 'number')
     tmpData = JSON.stringify(tmpData);
 
@@ -48,17 +48,17 @@ function toByteArray(data) {
 }
 
 // Compares NDEFRecords that were provided / received by the mock service.
 // TODO: Use different getters to get received record data,
 // see spec changes at https://github.com/w3c/web-nfc/pull/243.
 function compareNDEFRecords(providedRecord, receivedRecord) {
   assert_equals(providedRecord.recordType, receivedRecord.recordType);
 
-  // Compare media types without charset.
+  // Compares media types without charset.
   // Charset should be compared when watch method is implemented, in order
   // to check that written and read strings are equal.
   assert_equals(providedRecord.mediaType,
       receivedRecord.mediaType.substring(0, providedRecord.mediaType.length));
 
   assert_not_equals(providedRecord.recordType, 'empty');
 
   assert_array_equals(toByteArray(providedRecord.data),
@@ -171,43 +171,52 @@ var WebNFCTest = (() => {
       this.push_options_ = null;
       this.pending_promise_func_ = null;
       this.push_completed_ = true;
       this.push_should_timeout_ = false;
       this.client_ = null;
       this.watchers_ = [];
       this.reading_messages_ = [];
       this.operations_suspended_ = false;
+      this.is_ndef_tech_ = true;
+      this.is_formatted_tag_ = false;
     }
 
     // NFC delegate functions.
     async push(message, options) {
       let error = this.getHWError();
       if (error)
         return error;
-      // Cancel previous pending push operation.
+      // Cancels previous pending push operation.
       if (this.pending_promise_func_) {
         this.cancelPendingPushOperation();
       }
 
       this.pushed_message_ = message;
       this.push_options_ = options;
 
       return new Promise(resolve => {
         this.pending_promise_func_ = resolve;
-        // Pend push operation if NFC operation is suspended.
         if (this.operations_suspended_) {
-          // Do nothing, pends push operation.
+          // Pends push operation if NFC operation is suspended.
         } else if (options.timeout && options.timeout !== Infinity &&
             !this.push_completed_) {
-          // Resolve with TimeoutError, else pend push operation.
+          // Resolves with TimeoutError, else pend push operation.
           if (this.push_should_timeout_) {
             resolve(
                 createNDEFError(device.mojom.NDEFErrorType.TIMER_EXPIRED));
           }
+        } else if (!this.is_ndef_tech_) {
+          // Resolves with NotSupportedError if the device does not expose
+          // NDEF technology.
+          resolve(createNDEFError(device.mojom.NDEFErrorType.NOT_SUPPORTED));
+        } else if (this.is_formatted_tag_ && !options.overwrite) {
+          // Resolves with NotAllowedError if there are NDEF records on the device
+          // and overwrite is false.
+          resolve(createNDEFError(device.mojom.NDEFErrorType.NOT_ALLOWED));
         } else {
           resolve(createNDEFError(null));
         }
       });
     }
 
     async cancelPush(target) {
       if (this.push_options_ && ((target === device.mojom.NDEFPushTarget.ANY) ||
@@ -225,18 +234,19 @@ var WebNFCTest = (() => {
     async watch(options, id) {
       assert_true(id > 0);
       let error = this.getHWError();
       if (error) {
         return error;
       }
 
       this.watchers_.push({id: id, options: options});
-      // Ignore reading if NFC operation is suspended.
-      if(!this.operations_suspended_) {
+      // Ignores reading if NFC operation is suspended
+      // or the NFC tag does not expose NDEF technology.
+      if(!this.operations_suspended_ && this.is_ndef_tech_) {
         // Triggers onWatch if the new watcher matches existing messages.
         for (let message of this.reading_messages_) {
           if (matchesWatchOptions(message, options)) {
             this.client_.onWatch(
                 [id], fake_tag_serial_number, toMojoNDEFMessage(message));
           }
         }
       }
@@ -296,16 +306,18 @@ var WebNFCTest = (() => {
       this.hw_status_ = NFCHWStatus.ENABLED;
       this.push_completed_ = true;
       this.watchers_ = [];
       this.reading_messages_ = [];
       this.operations_suspended_ = false;
       this.cancelPendingPushOperation();
       this.bindingSet_.closeAllBindings();
       this.interceptor_.stop();
+      this.is_ndef_tech_ = true;
+      this.is_formatted_tag_ = false;
     }
 
     cancelPendingPushOperation() {
       if (this.pending_promise_func_) {
         this.pending_promise_func_(
             createNDEFError(device.mojom.NDEFErrorType.OPERATION_CANCELLED));
       }
 
@@ -314,19 +326,21 @@ var WebNFCTest = (() => {
       this.pending_promise_func_ = null;
       this.push_should_timeout_ = false;
       this.push_completed_ = true;
     }
 
     // Sets message that is used to deliver NFC reading updates.
     setReadingMessage(message) {
       this.reading_messages_.push(message);
-      // Ignore reading if NFC operation is suspended.
+      // Ignores reading if the NFC tag does not expose NDEF technology.
+      if(!this.is_ndef_tech_) return;
+      // Ignores reading if NFC operation is suspended.
       if(this.operations_suspended_) return;
-      // Ignore reading if NDEFPushOptions.ignoreRead is true.
+      // Ignores reading if NDEFPushOptions.ignoreRead is true.
       if(this.push_options_ && this.push_options_.ignoreRead)
         return;
       // Triggers onWatch if the new message matches existing watchers.
       for (let watcher of this.watchers_) {
         if (matchesWatchOptions(message, watcher.options)) {
           this.client_.onWatch(
               [watcher.id], fake_tag_serial_number,
               toMojoNDEFMessage(message));
@@ -345,38 +359,46 @@ var WebNFCTest = (() => {
     }
 
     // Resumes all suspended NFC operations.
     resumeNFCOperations() {
       this.operations_suspended_ = false;
       // Resumes pending NFC reading.
       for (let watcher of this.watchers_) {
         for (let message of this.reading_messages_) {
-          if (matchesWatchOptions(message, watcher.options)) {
+          if (matchesWatchOptions(message, watcher.options) && this.is_ndef_tech_) {
             this.client_.onWatch(
                 [watcher.id], fake_tag_serial_number,
                 toMojoNDEFMessage(message));
           }
         }
       }
       // Resumes pending push operation.
       if (this.pending_promise_func_) {
         this.pending_promise_func_(createNDEFError(null));
       }
     }
+
+    setIsNDEFTech(isNdef) {
+      this.is_ndef_tech_ = isNdef;
+    }
+
+    setIsFormattedTag(isFormatted) {
+      this.is_formatted_tag_ = isFormatted;
+    }
   }
 
   let testInternal = {
     initialized: false,
     mockNFC: null
   }
 
   class NFCTestChromium {
     constructor() {
-      Object.freeze(this); // Make it immutable.
+      Object.freeze(this); // Makes it immutable.
     }
 
     initialize() {
       if (testInternal.initialized)
         throw new Error('Call reset() before initialize().');
 
       testInternal.mockNFC = new MockNFC;
       testInternal.initialized = true;
--- a/testing/web-platform/tests/web-nfc/NDEFReader_scan.https.html
+++ b/testing/web-platform/tests/web-nfc/NDEFReader_scan.https.html
@@ -186,9 +186,39 @@ nfc_test(async (t, mockNFC) => {
 test(() => {
   const reader = new NDEFReader();
   invalid_signals.forEach(invalid_signal => {
     assert_throws(new TypeError(),
         () => { reader.scan({ signal: invalid_signal }); });
   });
 }, "NDEFReader.scan should fail if signal is not an AbortSignal.");
 
+nfc_test(async (t, mockNFC) => {
+  const reader = new NDEFReader();
+  mockNFC.setIsNDEFTech(false);
+  mockNFC.setReadingMessage(createMessage([createTextRecord(test_text_data)]));
+
+  await new Promise((resolve, reject) => {
+    reader.onreading = () => reject("reading event should not be fired.");
+    reader.scan();
+    t.step_timeout(resolve, 100);
+  });
+}, "Test that NDEFReader.onreading should not be fired if the NFC tag does not \
+expose NDEF technology.");
+
+nfc_test(async (t, mockNFC) => {
+  const reader = new NDEFReader();
+  const controller = new AbortController();
+  mockNFC.setReadingMessage({ records: [] });
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+
+  const promise = readerWatcher.wait_for("reading").then(event => {
+    assert_equals(event.serialNumber, fake_tag_serial_number);
+    assert_equals(event.message.records.length, 0);
+    controller.abort();
+  });
+  // NDEFReader#scan() asynchronously dispatches the reading event.
+  reader.scan({signal : controller.signal});
+  await promise;
+}, "Test that NDEFReader.onreading should be fired on an unformatted NFC tag \
+with empty records array for NDEFMessage.");
+
 </script>
--- a/testing/web-platform/tests/web-nfc/NDEFWriter_push.https.html
+++ b/testing/web-platform/tests/web-nfc/NDEFWriter_push.https.html
@@ -14,16 +14,20 @@ const invalid_type_messages =
     [
       // Invalid NDEFMessageSource type
       undefined,
 
       // NDEFMessage.records: should have at least 1 valid record.
       // https://w3c.github.io/web-nfc/#the-push-method - Step 8.
       createMessage([{}]),
 
+      // NDEFMessageSource: not NDEF-formatable.
+      // https://w3c.github.io/web-nfc/#the-push-method - Step 8.
+      createMessage([]),
+
       // https://w3c.github.io/web-nfc/#dfn-map-text-to-ndef
       // NDEFRecord must have data.
       createMessage([createTextRecord()]),
 
       // NDEFRecord.data for 'text' record must be a string.
       createMessage([createTextRecord(test_buffer_data)]),
       createMessage([createTextRecord(test_json_data)]),
       createMessage([createTextRecord(test_number_data)]),
@@ -437,9 +441,40 @@ nfc_test(async (t, mockNFC) => {
   const writer = new NDEFWriter();
   await writer.push({ records: [{ recordType: "w3.org:xyz", data: test_buffer_data }] });
   const message = createMessage([createRecord('w3.org:xyz', 'application/octet-stream',
       test_buffer_data)]);
   assertNDEFMessagesEqual(message, mockNFC.pushedMessage());
 }, "Test that mediaType should be set to 'application/octet-stream' if \
 NDEFRecordInit.record's recordType is external type and NDEFRecordInit.record's \
 mediaType is undefined.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NDEFWriter();
+  mockNFC.setIsNDEFTech(false);
+  await promise_rejects(t, 'NotSupportedError', writer.push(test_text_data));
+}, "NDEFWriter.push should fail when the NFC device does not expose \
+NDEF technology.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NDEFWriter();
+  await writer.push(test_text_data, { overwrite: false });
+  assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
+}, "NDEFWriter.push should succeed to push data to an unformatted NFC device \
+when the NDEFPushOptions.overwrite is false.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NDEFWriter();
+  await writer.push(test_buffer_data);
+  assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage());
+  await writer.push(test_text_data, { overwrite: true });
+  assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
+}, "NDEFWriter.push should succeed to overwrite the existing data \
+when the NDEFPushOptions.overwrite is true.");
+
+nfc_test(async (t, mockNFC) => {
+  const writer = new NDEFWriter();
+  const p = writer.push(test_text_data, { overwrite: false });
+  mockNFC.setIsFormattedTag(true);
+  await promise_rejects(t, 'NotAllowedError', p);
+}, "NDEFWriter.push should fail when there are NDEF records on the NFC device \
+and NDEFPushOptions.overwrite is false.");
 </script>
--- a/testing/web-platform/tests/web-nfc/README.md
+++ b/testing/web-platform/tests/web-nfc/README.md
@@ -12,16 +12,18 @@ The `WebNFCTest` interface is defined as
 
   class MockNFC {
     setHWStatus(number status); // Sets the hardware status.
     setReadingMessage(NDEFMessageInit message); // Sets message that is used to deliver NFC reading updates.
     setPendingPushCompleted(boolean result); // Sets if the pending push is completed.
     setPushShouldTimeout(boolean result); // Sets flag to trigger the pending push to timeout.
     pushedMessage(); // Gets the pushed `NDEFMessageSource`.
     pushOptions(); // Gets the pushed `NDEFPushOptions`.
+    setIsNDEFTech(boolean isNDEF); // Sets if the NFC device exposes NDEF technology.
+    setIsFormattedTag(boolean isFormatted); // Sets if the NFC tag has formatted NDEF message.
   };
 ```
 
 The Chromium implementation of the `WebNFCTest` interface is located in
 [nfc-mock.js](../resources/chromium/nfc-mock.js).
 
 Other browser vendors should provide their own implementations of
 the `WebNFCTest` interfaces.