# frozen_string_literal: true
# Copyright (c) 2010-2017 VMware, Inc.  All Rights Reserved.
# SPDX-License-Identifier: MIT

require 'test_helper'

class SerializationTest < Test::Unit::TestCase
  def check str, obj, type, array=false
    conn = VIM.new(ns: 'urn:vim25', rev: '4.0')
    xml = Builder::XmlMarkup.new indent: 2
    conn.obj2xml(xml, 'root', type, array, obj)

    begin
      assert_equal str, xml.target!
    rescue Test::Unit::AssertionFailedError
      puts 'expected:'
      puts str
      puts
      puts 'got:'
      puts xml.target!
      puts
      raise
    end
  end

  def test_moref
    check <<~EOS, VIM.Folder(nil, 'ha-folder-host'), 'Folder'
      <root type="Folder">ha-folder-host</root>
    EOS
  end

  def test_config
    cfg = VIM.VirtualMachineConfigSpec(
      name: 'vm',
      files: { vmPathName: '[datastore1]' },
      guestId: 'otherGuest64',
      numCPUs: 2,
      memoryMB: 3072,
      deviceChange: [
        {
          operation: :add,
          device: VIM.VirtualLsiLogicController(
            key: 1000,
            busNumber: 0,
            sharedBus: :noSharing
          )
        }, VIM.VirtualDeviceConfigSpec(
          operation: VIM.VirtualDeviceConfigSpecOperation(:add),
          fileOperation: VIM.VirtualDeviceConfigSpecFileOperation(:create),
          device: VIM.VirtualDisk(
            key: 0,
            backing: VIM.VirtualDiskFlatVer2BackingInfo(
              fileName: '[datastore1]',
              diskMode: :persistent,
              thinProvisioned: true
            ),
            controllerKey: 1000,
            unitNumber: 0,
            capacityInKB: 4000000
          )
        ), {
          operation: :add,
          device: VIM.VirtualE1000(
            key: 0,
            deviceInfo: {
              label: 'Network Adapter 1',
              summary: 'VM Network'
            },
            backing: VIM.VirtualEthernetCardNetworkBackingInfo(
              deviceName: 'VM Network'
            ),
            addressType: 'generated'
          )
        }
      ],
      extraConfig: [
        {
          key: 'bios.bootOrder',
          value: 'ethernet0'
        }
      ]
    )
    check <<~EOS, cfg, 'VirtualMachineConfigSpec'
      <root xsi:type="VirtualMachineConfigSpec">
        <name>vm</name>
        <guestId>otherGuest64</guestId>
        <files xsi:type="VirtualMachineFileInfo">
          <vmPathName>[datastore1]</vmPathName>
        </files>
        <numCPUs>2</numCPUs>
        <memoryMB>3072</memoryMB>
        <deviceChange xsi:type="VirtualDeviceConfigSpec">
          <operation>add</operation>
          <device xsi:type="VirtualLsiLogicController">
            <key>1000</key>
            <busNumber>0</busNumber>
            <sharedBus>noSharing</sharedBus>
          </device>
        </deviceChange>
        <deviceChange xsi:type="VirtualDeviceConfigSpec">
          <operation>add</operation>
          <fileOperation>create</fileOperation>
          <device xsi:type="VirtualDisk">
            <key>0</key>
            <backing xsi:type="VirtualDiskFlatVer2BackingInfo">
              <fileName>[datastore1]</fileName>
              <diskMode>persistent</diskMode>
              <thinProvisioned>1</thinProvisioned>
            </backing>
            <controllerKey>1000</controllerKey>
            <unitNumber>0</unitNumber>
            <capacityInKB>4000000</capacityInKB>
          </device>
        </deviceChange>
        <deviceChange xsi:type="VirtualDeviceConfigSpec">
          <operation>add</operation>
          <device xsi:type="VirtualE1000">
            <key>0</key>
            <deviceInfo xsi:type="Description">
              <label>Network Adapter 1</label>
              <summary>VM Network</summary>
            </deviceInfo>
            <backing xsi:type="VirtualEthernetCardNetworkBackingInfo">
              <deviceName>VM Network</deviceName>
            </backing>
            <addressType>generated</addressType>
          </device>
        </deviceChange>
        <extraConfig xsi:type="OptionValue">
          <key>bios.bootOrder</key>
          <value xsi:type="xsd:string">ethernet0</value>
        </extraConfig>
      </root>
    EOS
  end

  def test_nil_field
    obj = VIM.OptionValue(key: 'foo', value: nil)
    check <<~EOS, obj, 'OptionValue'
      <root xsi:type="OptionValue">
        <key>foo</key>
      </root>
    EOS
  end

  def test_string_array
    obj = ['foo', 'bar', 'baz']
    check <<~EOS, obj, 'xsd:string', true
      <root>foo</root>
      <root>bar</root>
      <root>baz</root>
    EOS
  end

  def test_int_array
    obj = [1, 2, 3]
    check <<~EOS, obj, 'xsd:int', true
      <root>1</root>
      <root>2</root>
      <root>3</root>
    EOS
  end

  def test_boolean_array
    obj = [true, false, true]
    check <<~EOS, obj, 'xsd:boolean', true
      <root>1</root>
      <root>0</root>
      <root>1</root>
    EOS
  end

  def test_float_array
    obj = [0.0, 1.5, 3.14]
    check <<~EOS, obj, 'xsd:float', true
      <root>0.0</root>
      <root>1.5</root>
      <root>3.14</root>
    EOS
  end

  def test_binary
    obj = "\x00foo\x01bar\x02baz"
    check <<~EOS, obj, 'xsd:base64Binary'
      <root>AGZvbwFiYXICYmF6</root>
    EOS
  end

  def test_property_spec
    interested = %w(info.progress info.state info.entityName info.error)
    tasks = [VIM::Task.new(nil, 'task-11')]
    obj = {
      propSet: [{ type: 'Task', all: false, pathSet: interested }],
      objectSet: tasks.map { |x| { obj: x } },
    }
    check <<~EOS, obj, 'PropertyFilterSpec'
      <root xsi:type="PropertyFilterSpec">
        <propSet xsi:type="PropertySpec">
          <type>Task</type>
          <all>0</all>
          <pathSet>info.progress</pathSet>
          <pathSet>info.state</pathSet>
          <pathSet>info.entityName</pathSet>
          <pathSet>info.error</pathSet>
        </propSet>
        <objectSet xsi:type="ObjectSpec">
          <obj type="Task">task-11</obj>
        </objectSet>
      </root>
    EOS

  end

  def test_keyvalue
    obj = RbVmomi::BasicTypes::KeyValue.new('a', 'b')
    check <<~EOS, obj, 'KeyValue', false
      <root>
        <key>a</key>
        <value>b</value>
      </root>
    EOS

    obj = ['a', 'b']
    check <<~EOS, obj, 'KeyValue', false
      <root>
        <key>a</key>
        <value>b</value>
      </root>
    EOS

    obj = [['a', 'b'], ['c', 'd']]
    check <<~EOS, obj, 'KeyValue', true
      <root>
        <key>a</key>
        <value>b</value>
      </root>
      <root>
        <key>c</key>
        <value>d</value>
      </root>
    EOS

    obj = { 'a' => 'b', c: 'd' }
    check <<~EOS, obj, 'KeyValue', true
      <root>
        <key>a</key>
        <value>b</value>
      </root>
      <root>
        <key>c</key>
        <value>d</value>
      </root>
    EOS
  end

  def test_ovf_import_spec_params
    obj = RbVmomi::VIM::OvfCreateImportSpecParams(
      hostSystem: VIM::HostSystem(nil, 'myhost'),
      locale: 'US',
      entityName: 'myvm',
      deploymentOption: '',
      networkMapping: [],
      propertyMapping: [['a', 'b'], ['c', 'd']],
      diskProvisioning: :thin
    )

    check <<~EOS, obj, 'OvfCreateImportSpecParams', false
      <root xsi:type="OvfCreateImportSpecParams">
        <locale>US</locale>
        <deploymentOption></deploymentOption>
        <entityName>myvm</entityName>
        <hostSystem type="HostSystem">myhost</hostSystem>
        <propertyMapping>
          <key>a</key>
          <value>b</value>
        </propertyMapping>
        <propertyMapping>
          <key>c</key>
          <value>d</value>
        </propertyMapping>
        <diskProvisioning>thin</diskProvisioning>
      </root>
    EOS
  end

  def test_datetime
    obj = DateTime.new(2011, 11, 16, 13, 36, 8, Rational(-8, 24))
    check <<~EOS, obj, 'xsd:dateTime', false
      <root>2011-11-16T13:36:08-08:00</root>
    EOS
  end

  def test_time
    obj = Time.at(DateTime.new(2011, 11, 16, 13, 36, 8, Rational(-8, 24)).strftime('%s').to_i).getgm
    check <<~EOS, obj, 'xsd:dateTime', false
      <root>2011-11-16T21:36:08Z</root>
    EOS
  end

  # TODO test all types
  def test_any_type
    obj = 1
    check <<~EOS, obj, 'xsd:anyType', false
      <root xsi:type="xsd:long">1</root>
    EOS

    obj = VIM::HostAccountSpec(id: 'root', password: 'foo')
    check <<~EOS, obj, 'xsd:anyType', false
      <root xsi:type="HostAccountSpec">
        <id>root</id>
        <password>foo</password>
      </root>
    EOS
  end
end
