2011 年 8 月 のアーカイブ

Symfonyの機能テストでclickを使わずにmultipartのファイルPOSTをする方法




















Symfony 1.4で実装したAPIのFunctionalテストをする際に、ファイルをPOSTする方法に迷ったので調べました。

ファイルのアップロードテストは、通常のWebサイトであれば、

$browser->
    get('/upload')->
    click('Upload', array('form' => array(
        'file1' => sfConfig::get('sf_test_dir').'/datas/test01.jpg',
    )))
;

みたいに書くのですが、テストしたいアプリケーションはAPIなので、GETするページはありません。

sfBrowserBase::clickを参考にして、こんな感じで実装しました。

まずはテスト本体。test/functional/api/hogeActionsTest.php

<?php

include(dirname(__FILE__).'/../../bootstrap/functional.php');

$browser = new MyTestFunctional(new myBrowser());
$browser->loadData();

$browser->
    info('JPEGファイルをアップロードできる')->
    setUploadFile('file1', sfConfig::get('sf_test_dir').'/datas/test01.jpg')->
    post('/upload', array('title' => 'hoge'))->
    
    with('response')->begin()->
        isStatusCode(200)->
    end()->
;

次にmyBrowserを定義します。lib/test/myBrowser.class.php

<?php

class myBrowser extends sfBrowser
{

    public function setUploadFile($key, $filename)
    {
        if (is_readable($filename))
        {
          $fileError = UPLOAD_ERR_OK;
          $fileSize = filesize($filename);
        }
        else
        {
          $fileError = UPLOAD_ERR_NO_FILE;
          $fileSize = 0;
        }
        
        $this->parseArgumentAsArray($key, array(
            'name' => basename($filename),
            'type' => '',
            'tmp_name' => $filename,
            'error' => $fileError,
            'size' => $fileSize,
        ),
        $this->files);
    }

}

sfBrowserBaseクラスのfilesに対して配列でファイルの情報を書き込むと、POST直前に$_FILESに書きこんでくれます。

で、あとはMyでmyBrowser::setUploadFileを呼び出すだけです。lib/test/MyTestFunctional.class.php

<?php

class MyTestFunctional extends sfTestFunctional
{
    public function loadData()
    {
        $doctrine = Doctrine_Manager::getInstance()->getCurrentConnection()->getDbh();
        $doctrine->query('SET FOREIGN_KEY_CHECKS = 0');
        $doctrine->query('TRUNCATE TABLE uploads');
        $doctrine->query('SET FOREIGN_KEY_CHECKS = 1');
        unset($doctrine);
        
        Doctrine::loadData(sfConfig::get('sf_test_dir').'/fixtures/');
        return $this;
    }

    public function setUploadFile($key, $filename)
    {
        $this->browser->setUploadFile($key, $filename);
        
        return $this;
    }
}

こうやってsetUploadFile(…)->post(…)と呼び出せばOKです。

sfBrowserBase::callされるたびにfilesの中身はクリアされます。


SSDのデータを完全消去する




















SSDを廃棄したり、人に譲るときなど、データを完全に削除したい場合があります。

HDDで使われるディスク消去ツールは、HDDの特性に合わせて作られているので、SSDで実行した場合に、限られた書き込み回数を無駄に消費することになるので使えません。

Intelから提供されているSSD Toolboxを使うと、簡単にデータを削除できます。

ダウンロード・センター Intel® Solid State Drive Toolbox

Windows 7,Windows Vista,Windows XPで使えます。


ArduinoとEthernetシールドとDHCPとDNS




















以前書いた Arduino DHCP Libraryを使ってみる : blog.loadlimit – digital matter – で使ったライブラリが、最近のIDEで使えなくなったということで、他のライブラリを見つけました。

gkaindl.com → software → arduino ethernet

前述のDHCP Libraryをベースに改良されたもので、機能も拡張されています。

ダウンロード後、解凍して、中身のフォルダをArduinoのlibrariesフォルダにコピーします。

あとはただただソースを書くだけ。

DHCPの更新処理とかを自動でやってくれるmaintain()が便利です。こんな感じのコードになりました。
#if defined(ARDUINO) && ARDUINO > 18
#include <SPI.h>
#endif
#include <Ethernet.h>
#include <EthernetDHCP.h>
#include <EthernetDNS.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

const char* hostName = "sample.com";

void setup()
{
  delay(100);
  Serial.begin(9600);

  EthernetDHCP.begin(mac);
}

void loop()
{
  EthernetDHCP.maintain();
  
  const byte* dnsAddr = EthernetDHCP.dnsIpAddress();
  EthernetDNS.setDNSServer(dnsAddr);
  
  static bool isDnsResolved = false;
  static byte remoteAddr[4];
  
  if (!isDnsResolved) {
    
    DNSError err = EthernetDNS.sendDNSQuery(hostName);
    
    if (DNSSuccess == err) {
      do {
        err = EthernetDNS.pollDNSReply(remoteAddr);
        
        if (DNSTryLater == err) {
          delay(20);
          Serial.print(".");
        }
      } while (DNSTryLater == err);
    }
    
    if (DNSSuccess == err) {
      isDnsResolved = true;
    }
    
  }
  
  if (isDnsResolved) {
    
    Client client(remoteAddr, 80);
    
    Serial.println("connecting...");
    
    if (client.connect()) {
      Serial.println("connected");
      
      client.println("GET / HTTP/1.0");
      client.println();
      
      // 返事が戻ってくるまで待機
      do {
        if (client.available()) {
          char c = client.read();
          // ここで何かする
        }
        if (!client.connected()) {
          Serial.println();
          Serial.println("disconnecting.");
          client.stop();
          break;
        }
      } while (true);
    }
    else {
      Serial.println("connection failed");
      delay(2000);
    }
    
  }
}

DNSの更新は見ていないのでDDNS環境のサーバを長時間監視する場合はそれなりの処理が必要です。

それと、イーサネットシールドがPCに接続していない場合にIPを取れない問題は、新しいバージョンのイーサネットシールドでは修正されていたようです。

スイッチサイエンス/商品詳細 Arduino イーサネットシールド

古いバージョンを使っている人は、下記のサイトに対策が載っていました。自分もコンデンサひとつとダイオードひとつ追加してみたら正常に動作するようになりました。

Arduino Ethernet Shieldのパワーオンリセット: PS3とLinux、電子工作も