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

Unit tests for the project #8

Open
mpmartins opened this issue Apr 12, 2020 · 3 comments
Open

Unit tests for the project #8

mpmartins opened this issue Apr 12, 2020 · 3 comments

Comments

@mpmartins
Copy link
Contributor

Hey @yuval-k, Happy easter.

The more I touch this project, the more I get excited about the possibilities... :D

I wanted to open a discussion with you about the project unit tests/integration tests.

First, just wanted to say that we need to find a way to create some unit tests for the project. I couldn't find the time to study that in detail, but maybe we can use a library like https://github.com/jtenner/as-pect.

But, what I really wanted to share is that I converted your example of integration tests implemented in an HTML file to a javascript file that can be triggered using node. Take a look at my example below:

const assert = require("assert");
const fs = require("fs");
const loader = require("@assemblyscript/loader");
const util = require('util');
const utf8Decoder = new util.TextDecoder('utf-8');

/**
 * This function is used to inject values into Web Assembly VM memory.
 * 
 * @param {String} value - value to be injected into WebAssembly memory
 * @param {Number} value_ptr_ptr - pointer to the memory address that starts this variable
 * @param {Number} value_size_ptr - pointer to the memory address with variable size
 */
function injectStringIntoMemory(value, value_ptr_ptr, value_size_ptr) { 
  
  const value_size = value.length;
  let bytes = globalExports.malloc(value_size);
  if (bytes === 0) {
      throw "can't allocate";
  }

  var moduleMemory = globalExports.memory.buffer;

  var confMem = new Uint8Array(moduleMemory, bytes, value_size);
  for(let i = 0; i < value_size; i++){
      confMem[i]=value.charCodeAt(i);
  }

  var confPtr = new Uint32Array(moduleMemory, value_ptr_ptr, 1);
  confPtr[0] = bytes;
  var confSizePtr = new Uint32Array(moduleMemory, value_size_ptr, 1);
  confSizePtr[0] = value_size;

  console.info("injectStringIntoMemory: " + value + " to " + value_ptr_ptr + ":" + value_size_ptr);
}

//Mocks and hardcoded values
const root_id_string = "expo_filter_root_id"
const config = "expo_filter_configuration_value1";

const imports = {
  wasi_unstable: {
    proc_exit: console.trace
  },
  env: {
    memory: new WebAssembly.Memory({
      initial: 1,
      maximum: 1
    }),
    abort: console.trace,
    proxy_log(level, logMessage, messageSize) {
      var mem = new Uint8Array(globalExports.memory.buffer);
      let buf = mem.slice(logMessage, logMessage + messageSize);
      let msg = "proxy_log: " + utf8Decoder.decode(buf);
      switch(level) { 
        case 1: { 
          console.debug(msg); 
          break; 
        } 
        case 2: { 
          console.info(msg);
          break; 
        } 
        case 3: { 
          console.warn(msg);
          break; 
        } 
        case 4: { 
          console.error(msg);
          break; 
        } 
        default: { 
          console.trace(msg); 
          break; 
        } 
      }
      return 0;
    },
    proxy_get_configuration(configuration_ptr, configuration_size) {
      console.debug("proxy_get_configuration: " + config + ":" + configuration_ptr + ":" + configuration_size);
      injectStringIntoMemory(config, configuration_ptr, configuration_size);
      return config;
    },
    proxy_get_property(path_ptr, path_size, value_ptr_ptr, value_size_ptr) { 
      // we only support one property now, the root id.
      const prop = root_id_string;
      console.debug("proxy_get_property: " + prop + ":" + path_ptr + "-" + path_size + ":" + value_ptr_ptr + "-" + value_size_ptr);
      injectStringIntoMemory(prop, value_ptr_ptr, value_size_ptr);
      return prop;
    },
    proxy_get_status: console.trace,
    proxy_set_tick_period_milliseconds: console.trace,
    proxy_get_current_time_nanoseconds: console.trace,
    proxy_set_property: console.trace,
    proxy_continue_request: console.trace,
    proxy_continue_response: console.trace,
    proxy_send_local_response: console.trace,
    proxy_clear_route_cache: console.trace,
    proxy_get_shared_data: console.trace,
    proxy_set_shared_data: console.trace,
    proxy_register_shared_queue: console.trace,
    proxy_resolve_shared_queue: console.trace,
    proxy_dequeue_shared_queue: console.trace,
    proxy_enqueue_shared_queue: console.trace,
    proxy_add_header_map_value: console.trace,
    proxy_get_header_map_value: console.trace,
    proxy_get_header_map_pairs: console.trace,
    proxy_set_header_map_pairs: console.trace,
    proxy_replace_header_map_value: console.trace,
    proxy_remove_header_map_value: console.trace,
    proxy_get_header_map_size: console.trace,
    proxy_get_buffer_bytes: console.trace,
    proxy_get_buffer_status: console.trace,
    proxy_http_call: console.trace,
    proxy_grpc_call: console.trace,
    proxy_grpc_stream: console.trace,
    proxy_grpc_cancel: console.trace,
    proxy_grpc_close: console.trace,
    proxy_grpc_send: console.trace,
    proxy_define_metric: console.trace,
    proxy_increment_metric: console.trace,
    proxy_record_metric: console.trace,
    proxy_get_metric: console.trace,
    proxy_set_effective_context: console.trace,
    proxy_done: console.trace
  }
}

/**
 * Simple Unit test to check if the Filter can be initialized using wasm and making calls the proxy makes to initialize them.
 */
function testModuleLoading() {
  var wasm = loader.instantiateSync(fs.readFileSync(__dirname + "/../build/untouched.wasm"), imports);

  let exports = wasm;
  globalExports = exports;
  let context_id = 0;
  const root_context_id = context_id++;

  // Proxy calling Filter to initialize context
  exports.proxy_on_context_create(context_id, root_context_id);

  // Proxy calling Filter when VM is starting
  assert(exports.proxy_on_vm_start(context_id, root_context_id) === 1, "Failed to start VM.");

  // Initializing Filter
  assert(exports.proxy_on_configure(context_id++, config.length) === 1, "Failed to start filter"); 

  fs.writeFileSync("tests/memory.dump", new DataView(exports.memory.buffer, 0, exports.memory.buffer.byteLength));

  //Trying to trigger a request to the filter
  //const headers = [{"x-wmt-expo":"test"}];
  //exports.proxy_on_context_create(context_id, root_context_id);
  //let result = exports.proxy_on_request_headers(context_id, null);
  //console.log(result);
};

testModuleLoading();

This way, we can trigger the tests by just running the "node tests" command.

Thanks,
Mario

@yuval-k
Copy link
Member

yuval-k commented Apr 13, 2020

nice , thank you - this is very helpful!
i will look into the link you attached for unit testing - I was hoping AS will gain ability for interfaces soon and then i can cleanup a lot of the workarounds i had due to the lack of it, and also mock properly. I will look at the link and see how we can integrate this

@yuval-k
Copy link
Member

yuval-k commented Apr 16, 2020

i added some basic ci, that builds the example filter.
next i am hoping to integrate the integration test as outlined above; PRs welcome!

@mpmartins
Copy link
Contributor Author

Hey, sorry for not updating this. I will try to find some time this week to create a PR to add this test to the example plugins you created.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants