2012年8月6日 星期一

用 Tcl 實作 BBS Client - TclTelnet

SNAGHTML2f9694

前言

PTT 已經是台灣最重要的資訊交流地之一,不管是美食、O2、八卦、表特…等,在這邊都可以找到許多資源。但若用PCMAN、KKMAN等telnet軟體連線,都只能人工看,無法做到自動觀察的行為。

(如果有[徵男]出現,當然要第一時間看文章,做出回應呀!)

 

Github 連結

https://github.com/bmcool/TclTelnet

 

安裝方式

與一般 Tcl package 無異,解壓縮至 Tcl/lib 底下即可。

 

使用範例

package require TclTelnet
 
# New 一個 TclTclnet 物件 telnet
::TclTelnet::TclTelnet telnet
 
# 進行連線
telnet connect ptt.cc
 
# 送出 myusername 字元,並按下 Enter
telnet sendLine myusername
 
# 送出 myusername 字元,並按下 Enter
telnet sendLine mypassword
 
# 按下「上」
telnet press up
 
# 取得畫面 80 x 24 的字串
telnet printScreen
 

其他更詳細的 method 可以直接閱讀 TclTelnet.xotcl

2012年6月5日 星期二

NSData to Hex String & Hex String to NSData

Quick Note
// open file to NSData

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"image" ofType:@"png"];

NSData *imageData = [NSData dataWithContentsOfFile:filePath];

NSLog(@"imageData = %@", imageData);

 

// NSData to hexString

NSString *hexString = [[imageData description] stringByReplacingOccurrencesOfString:@" " withString:@""];

hexString = [hexString stringByReplacingOccurrencesOfString:@"<" withString:@""];

hexString = [hexString stringByReplacingOccurrencesOfString:@">" withString:@""];

NSLog(@"hexString = %@", hexString);

 

// hexString to NSData

NSMutableData *data = [[NSMutableData alloc] init];

unsigned char whole_byte;

char byte_chars[3] = {'\0','\0','\0'};

int i;

for (i = 0; i < ([hexString length] / 2); i++) {

    byte_chars[0] = [hexString characterAtIndex:i*2];

    byte_chars[1] = [hexString characterAtIndex:i*2+1];

    whole_byte = strtol(byte_chars, NULL, 16);

    [data appendBytes:&whole_byte length:1]; 

}

NSLog(@"data = %@", data);

2012年3月24日 星期六

在 Windows Phone 上讀取 BIG5 網頁

SNAGHTML2b39c0e

Windows Phone SDK 是不支援 BIG5 編碼的,它只支援三種編碼。

BigEndianUnicode、Unicode、UTF8

image 

 

因此若要讀取 BIG5 的網頁,就必須自行將 BIG5 轉換為 Unicode,實作的主要重點在於:

  1. 取得 BIG5 –> Unicode 轉換表。(BIG5.TXT)
  2. 將轉換表改用 Dictionary 型態儲存。
  3. 讀取網頁時,用 stream,不要用 WebClient!

使用 WebClient 來讀取網頁,得到的並非是 raw data,而是已被 default encoding 轉換過的 data。更進一步解釋,就是使用了 UTF8 –> Unicode 轉換表來轉換 BIG5 的資料,這會導致資料整個變成不可用的亂碼。

 

實作

將 BIG5.TXT 加入專案

image 

 

在 ContentPanel 加入一個 textBlock

image

 

MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
 
using System.IO;
using System.Globalization;
using System.Diagnostics;
using System.Text;
 
namespace PhoneApp2
{
    public partial class MainPage : PhoneApplicationPage
    {
        // async http
        delegate void DownDelegate(string content);
        DownDelegate downDelegate;
 
        // Big5 to Unicode mapping table
        private static Dictionary<int, int> mBIG5_Unicode_MAP = new Dictionary<int, int>();
 
        // 建構函式
        public MainPage()
        {
            InitializeComponent();
            createBig5ToUnicodeDictionary();
            readBig5WebPage();
        }
 
        private void setConent(string content)
        {
            textBlock1.Text = content;
        }
 
        private void createBig5ToUnicodeDictionary()
        {
            var resource = Application.GetResourceStream(new Uri("BIG5.TXT", UriKind.Relative));
            StreamReader sr = new StreamReader(resource.Stream);
            string line;
            while ((line = sr.ReadLine()) != null)
            {
                // 忽略註解
                if (line.StartsWith("#")) continue;
                string[] lTokens = line.Split(new char[] {'\t'});
                mBIG5_Unicode_MAP.Add(hexToInt(lTokens[0].Substring(2)), hexToInt(lTokens[1].Substring(2)));
            }
        }
 
        private void readBig5WebPage()
        {
            textBlock1.Text = "讀取中...";
            string url = "http://www.businessweekly.com.tw/feednews.php";
            downDelegate = setConent;
            System.Net.WebRequest request = HttpWebRequest.Create(url);
            IAsyncResult result = request.BeginGetResponse(ResponseCallback, request);
        }
 
        private void ResponseCallback(IAsyncResult result)
        {
            HttpWebRequest request = (HttpWebRequest)result.AsyncState;
            WebResponse response = request.EndGetResponse(result);
            Stream s = response.GetResponseStream();
            Dispatcher.BeginInvoke(downDelegate, big5ToUnicode(s).ToString());
        }
 
        private StringBuilder big5ToUnicode(Stream s)
        {
            StringBuilder lSB = new StringBuilder();
            byte[] big5Buffer = new byte[2];
            int input;
            while ((input = s.ReadByte()) != -1)
            {
                if (input > 0x81 && big5Buffer[0] == 0)
                {
                    big5Buffer[0] = (byte)input;
                }
                else if (big5Buffer[0] != 0)
                {
                    big5Buffer[1] = (byte)input;
                    int Big5Char = (big5Buffer[0] << 8) + big5Buffer[1];
                    try
                    {
                        int UTF8Char = mBIG5_Unicode_MAP[Big5Char];
                        lSB.Append((char)UTF8Char);
                    }
                    catch (Exception)
                    {
                        lSB.Append((char)mBIG5_Unicode_MAP[0xA148]);
                    }
 
                    big5Buffer = new byte[2];
                }
                else
                {
                    lSB.Append((char)input);
                }
            }
            return lSB;
        }
 
        private int hexToInt(string hexString)
        {
            return int.Parse(hexString, NumberStyles.HexNumber);
        }
    }
}

 

範例程式碼下載

 

參考連結

2011年12月10日 星期六

My iOS UI Automation Testing(自動化測試)

PastedGraphic-1
自動化測試是程式開發中,不可或缺的一環,而 xcode Instruments 裡面的 Automation tool ,便是 iOS UI 的自動化測試工具。
Automation tool 的測試腳本中,可以看得出來編譯完成的 APP,是符合 DOM(Document Object Model) 的。所以測試腳本,也很理所當然找了它最老的夥伴 - Javascript 來調用。

往下閱讀之前,建議一定要先看過...

DOM 雖然強大,但寫測試時卻又嫌囉嗦,要準確取得元件是一件很麻煩的事,看完上述連結,可以知道有兩種取得元件的方式:
  1. 用位置來取得,例如「window.buttons()[0]」,指的是第一個建立出來的 button。
  2. 用「name」來取得,例如「window.buttons()[“login”]」。
第一種方式,元件數量少還好,元件一多,會很容易指錯,而且測試程式碼可讀性幾乎是0,根本不知道到底取得的是哪一個元件。
第二種方式是比較理想的,但必須在 xib 上,或是 Objective-C 程式碼中,另外指定 accessibilityLabel 來命名 DOM 這邊所謂的「name」,也是挺麻煩的。
上述連結有這兩種方式的圖文說明。

突然想到之前寫 RSpec(Ruby 的自動測試框架) 測試 RoR 的時候,所有的操作,都只需要直接使用「肉眼所看到的字」,便可正確取得元件的文字或是進行操作(如點擊),於是便動手進行實作,簡化測試的程式。

https://github.com/alexvollmer/tuneup_js
借用了上述的測試框架,將自己寫的整合了進去,主要寫了三個 function
function haveContent(text) {

    var elements = window.elements();

    

    for (i = 0; i < elements.length; i++) {

        var element = elements[i];

        if (element.name() == text && element.isValid()) {

            return true;

        }

    }

    return false;

}

 

function click_button(name) {

    var buttons = window.buttons();

    

    for (i = 0; i < buttons.length; i++) {

        var button = buttons[i];

        if (button.name() == name) {

            button.tap();

        }

    }

}

 

function fill_in(name, text) {

    var textFields = window.textFields();

    

    for (i = 0; i < textFields.length; i++) {

        var textField = textFields[i];

        if (textField.name() == name) {

            textField.setValue(text);

        }

    }

}

haveContentclick_button 這兩個 function,只需指定肉眼看得到的文字就好,例如
test("Clear Hello", function(app, target) {

     click_button("Clear All");

     assertFalse(haveContent("Hello World"));

});

fill_in 則還是得在 xib 上,或是  Objective-C 程式碼中,另外指定 accessibilityLabel ,讓測試程式可以準確的指定到輸入框中,例如
test("Say Hello", function(app, target) {

     fill_in("name", "World");

     click_button("Say Hello");

     assertTrue(haveContent("Hello World"));

});

其實這三個 function 就可以搞定 95% 的測試了,其餘等需要時再補上,提供完整的範例下載

其它參考資源: