/*
* Copyright 2007 Yusuke Yamamoto
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package twitter4j;
import junit.framework.Assert;
import twitter4j.internal.async.DispatcherFactory;
import twitter4j.json.DataObjectFactory;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author Yusuke Yamamoto - yusuke at mac.com
* @since Twitter4J 2.1.9
*/
public class UserStreamTest extends TwitterTestBase implements UserStreamListener {
public UserStreamTest(String name) {
super(name);
}
protected void setUp() throws Exception {
super.setUp();
}
protected void tearDown() throws Exception {
super.tearDown();
}
private User source;
private User target;
Exception ex;
public void testUserStreamEventTypes() throws Exception {
InputStream is = TwitterTestBase.class.getResourceAsStream("/streamingapi-event-testcase.json");
UserStream stream = new UserStreamImpl(new DispatcherFactory().getInstance(), is, conf1);
source = null;
target = null;
ex = null;
stream.next(this);
waitForStatus("follow");
Assert.assertEquals(23456789, source.getId());
Assert.assertEquals(12345678, target.getId());
Assert.assertNull(ex);
source = null;
target = null;
ex = null;
stream.next(this);
waitForStatus("delete direct message");
assertReceived("onDeletionNotice-directmessage", TwitterMethod.DESTROY_DIRECT_MESSAGE);
// This one is an unknown event type. We should safely ignore it.
stream.next(this);
waitForStatus("unknown");
Assert.assertNull(source);
Assert.assertNull(target);
Assert.assertNull(ex);
}
public void testUserStream() throws Exception {
TwitterStream twitterStream = new TwitterStreamFactory(conf1).getInstance();
twitterStream.addListener(this);
try {
twitter1.destroyBlock(id2.id);
} catch (TwitterException ignore) {
}
try {
twitter2.destroyBlock(id1.id);
} catch (TwitterException ignore) {
}
try {
twitter1.createFriendship(id2.id);
} catch (TwitterException ignore) {
}
try {
twitter2.createFriendship(id1.id);
} catch (TwitterException ignore) {
}
//twit4j: id1.id
//twit4j2: 6377362
twitterStream.user(new String[]{"BAh7CToPY3JlYXR"});
//expecting onFriendList for twit4j and twit4j2
waitForStatus("friend list");
assertReceived("onfriendlist", "onfriendlist");
DirectMessage dm = twitter2.sendDirectMessage(id1.id, "test " + new Date());
waitForStatus("sentDirectMessage");
assertReceived("onDirectMessage", TwitterMethod.SEND_DIRECT_MESSAGE);
twitter1.destroyDirectMessage(dm.getId());
waitForStatus("destroyedDirectMessage");
Status status = twitter2.updateStatus("@twit4j " + new Date());
//expecting onStatus for twit4j from twit4j
waitForStatus("onStatus");
assertReceived("onstatus", "onstatus");
twitter1.retweetStatus(status.getId());
waitForStatus("onStatus");
assertReceived("onstatus", "onstatus");
twitter1.createFavorite(status.getId());
waitForStatus("createdFavorite");
assertReceived("onFavorite", TwitterMethod.CREATE_FAVORITE);
twitter1.destroyFavorite(status.getId());
waitForStatus("destroyedFavorite");
assertReceived("onUnfavorite", TwitterMethod.DESTROY_FAVORITE);
// unfollow twit4j
twitter1.destroyFriendship(id2.id);
waitForStatus("destroyedFriendship");
// follow twit4j
twitter1.createFriendship(id2.id);
waitForStatus("createdFriendship");
assertReceived("onFollow", TwitterMethod.CREATE_FRIENDSHIP);
status = twitter1.updateStatus("somerandometext " + new Date());
waitForStatus("updatedStatus");
assertReceived("onstatus", "onstatus");
twitter1.destroyStatus(status.getId());
waitForStatus("destroyedStatus");
assertReceived("onDeletionNotice-status", TwitterMethod.DESTROY_STATUS);
// block twit4j2
twitter1.createBlock(id2.id);
waitForStatus("createdBlock");
assertReceived("onBlock", TwitterMethod.CREATE_BLOCK);
// unblock twit4j2
twitter1.destroyBlock(id2.id);
waitForStatus("destroyedBlock");
assertReceived("onUnblock", TwitterMethod.DESTROY_BLOCK);
twitter1.updateProfile(null, null, new Date().toString(), null);
waitForStatus("updateProfile");
assertReceived("onUserProfileUpdated", TwitterMethod.UPDATE_PROFILE);
UserList list = twitter1.createUserList("test", true, "desctription");
waitForStatus("createdUserList");
assertReceived("onUserListCreated", TwitterMethod.CREATE_USER_LIST);
list = twitter1.updateUserList(list.getId(), "test2", true, "description2");
waitForStatus("updatedUserList");
assertReceived("onUserListUpdated", TwitterMethod.UPDATE_USER_LIST);
twitter1.createUserListMember(list.getId(), id2.id);
waitForStatus("addedListMember");
assertReceived("onUserListMemberAddition", TwitterMethod.CREATE_LIST_MEMBER);
twitter2.createUserListSubscription(list.getId());
waitForStatus("createdUserListSubscription");
assertReceived("onUserListSubscription", TwitterMethod.SUBSCRIBE_LIST);
twitter1.destroyUserListMember(list.getId(), id2.id);
waitForStatus("deletedUserListMember");
assertReceived("onUserListMemberDeletion", TwitterMethod.DESTROY_LIST_MEMBER);
twitter2.destroyUserListSubscription(list.getId());
waitForStatus("destroiedUserListSubscription");
assertReceived("onUserListUnsubscription", TwitterMethod.UNSUBSCRIBE_LIST);
twitter1.destroyUserList(list.getId());
waitForStatus("destroyedUserList");
assertReceived("onUserListDestoyed", TwitterMethod.DESTROY_USER_LIST);
assertReceived("onDeletionNotice-directmessage", TwitterMethod.DESTROY_DIRECT_MESSAGE);
// confirm if tracking term is effective
boolean found = false;
for (Object[] event : this.received) {
if ("onstatus".equals(event[0])) {
Status status1 = (Status) event[1];
if (-1 != status1.getText().indexOf("somerandometext")) {
found = true;
break;
}
}
}
Assert.assertTrue(found);
}
private void assertReceived(String assertion, Object obj) {
boolean received = false;
for (Object[] event : this.received) {
if (obj.equals(event[0])) {
received = true;
break;
}
}
Assert.assertTrue(assertion, received);
}
private synchronized void waitForStatus(String waitFor) {
System.out.println("waiting for:" + waitFor);
try {
this.wait(20000);
System.out.println(received.size() + " events received so far. last notification:" + received.get(received.size() - 1)[0]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void testDisplayURLNullCase() throws Exception {
// http://jira.twitter4j.org/browse/TFJ-704
// https://gist.github.com/4071950#file_2.parse_error_json_example.txt
// sometimes display_url is null and display_url doesn't exist
String rawJSON = "{ \"geo\": null, \"in_reply_to_screen_name\": null, \"favorited\": false, \"text\": \"RT @fxThailandfans: RT @princezephyr: [PRESS PICS] 121114 f(x) - KOR-AUS Football Match Half-time Show http:\\/\\/t.co\\/cbm1aPCU http:\\/\\/t.co\\/ ...\", \"possibly_sensitive\": false, \"in_reply_to_status_id_str\": null, \"created_at\": \"Wed Nov 14 12:03:02 +0000 2012\", \"in_reply_to_user_id_str\": null, \"retweet_count\": 2, \"coordinates\": null, \"source\": \"<a href=\\\"http:\\/\\/blackberry.com\\/twitter\\\" rel=\\\"nofollow\\\">Twitter for BlackBerry\\u00ae<\\/a>\", \"entities\": { \"hashtags\": [ ], \"user_mentions\": [ { \"indices\": [ 3, 18 ], \"screen_name\": \"fxThailandfans\", \"id_str\": \"563934022\", \"name\": \"fx_thailand\", \"id\": 563934022 }, { \"indices\": [ 23, 36 ], \"screen_name\": \"princezephyr\", \"id_str\": \"251443396\", \"name\": \"princezephyr\", \"id\": 251443396 } ], \"urls\": [ { \"indices\": [ 103, 123 ], \"display_url\": \"twitpic.com\\/bd46lk\", \"url\": \"http:\\/\\/t.co\\/cbm1aPCU\", \"expanded_url\": \"http:\\/\\/twitpic.com\\/bd46lk\" }, { \"indices\": [ 124, 136 ], \"url\": \"http:\\/\\/t.co\\/\", \"expanded_url\": null } ] }, \"place\": null, \"retweeted\": false, \"truncated\": false, \"id_str\": \"268685470089764864\", \"retweeted_status\": { \"geo\": null, \"in_reply_to_screen_name\": null, \"favorited\": false, \"text\": \"RT @princezephyr: [PRESS PICS] 121114 f(x) - KOR-AUS Football Match Half-time Show http:\\/\\/t.co\\/cbm1aPCU http:\\/\\/t.co\\/UW5HBXHn (7)\", \"possibly_sensitive\": false, \"in_reply_to_status_id_str\": null, \"created_at\": \"Wed Nov 14 11:42:13 +0000 2012\", \"in_reply_to_user_id_str\": null, \"retweet_count\": 2, \"coordinates\": null, \"source\": \"<a href=\\\"http:\\/\\/www.tweetdeck.com\\\" rel=\\\"nofollow\\\">TweetDeck<\\/a>\", \"entities\": { \"hashtags\": [ ], \"user_mentions\": [ { \"indices\": [ 3, 16 ], \"screen_name\": \"princezephyr\", \"id_str\": \"251443396\", \"name\": \"princezephyr\", \"id\": 251443396 } ], \"urls\": [ { \"indices\": [ 83, 103 ], \"display_url\": \"twitpic.com\\/bd46lk\", \"url\": \"http:\\/\\/t.co\\/cbm1aPCU\", \"expanded_url\": \"http:\\/\\/twitpic.com\\/bd46lk\" }, { \"indices\": [ 104, 124 ], \"display_url\": \"twitpic.com\\/bd46tf\", \"url\": \"http:\\/\\/t.co\\/UW5HBXHn\", \"expanded_url\": \"http:\\/\\/twitpic.com\\/bd46tf\" } ] }, \"place\": null, \"retweeted\": false, \"truncated\": false, \"id_str\": \"268680229562744832\", \"contributors\": null, \"in_reply_to_user_id\": null, \"in_reply_to_status_id\": null, \"user\": { \"friends_count\": 44, \"profile_link_color\": \"939AED\", \"followers_count\": 834, \"is_translator\": false, \"default_profile\": false, \"follow_request_sent\": null, \"contributors_enabled\": false, \"time_zone\": \"Bangkok\", \"created_at\": \"Thu Apr 26 18:22:34 +0000 2012\", \"profile_background_color\": \"C0DEED\", \"profile_background_tile\": true, \"profile_background_image_url_https\": \"https:\\/\\/si0.twimg.com\\/profile_background_images\\/576266919\\/0bvixbmvg2lj69ajtg6i.jpeg\", \"url\": \"http:\\/\\/www.fxthailand.com\\/\", \"description\": \"f(x) Thailand Fans Board.\\r\\n\\r\\nhttps:\\/\\/www.facebook.com\\/Fxthailandfans\", \"profile_sidebar_fill_color\": \"DDEEF6\", \"default_profile_image\": false, \"lang\": \"en\", \"favourites_count\": 1, \"profile_sidebar_border_color\": \"C0DEED\", \"profile_image_url_https\": \"https:\\/\\/si0.twimg.com\\/profile_images\\/2592067376\\/3q4797y4mhfcvh7b7k14_normal.png\", \"location\": \"\", \"id_str\": \"563934022\", \"verified\": false, \"notifications\": null, \"protected\": false, \"screen_name\": \"fxThailandfans\", \"following\": null, \"geo_enabled\": false, \"profile_use_background_image\": true, \"profile_image_url\": \"http:\\/\\/a0.twimg.com\\/profile_images\\/2592067376\\/3q4797y4mhfcvh7b7k14_normal.png\", \"name\": \"fx_thailand\", \"profile_text_color\": \"333333\", \"id\": 563934022, \"listed_count\": 6, \"statuses_count\": 1840, \"profile_background_image_url\": \"http:\\/\\/a0.twimg.com\\/profile_background_images\\/576266919\\/0bvixbmvg2lj69ajtg6i.jpeg\", \"utc_offset\": 25200 }, \"id\": 268680229562744832, \"possibly_sensitive_editable\": true }, \"contributors\": null, \"in_reply_to_user_id\": null, \"in_reply_to_status_id\": null, \"user\": { \"friends_count\": 96, \"profile_link_color\": \"0084B4\", \"followers_count\": 62, \"is_translator\": false, \"default_profile\": true, \"follow_request_sent\": null, \"contributors_enabled\": false, \"time_zone\": null, \"created_at\": \"Tue Jul 17 12:29:08 +0000 2012\", \"profile_background_color\": \"C0DEED\", \"profile_background_tile\": false, \"profile_background_image_url_https\": \"https:\\/\\/si0.twimg.com\\/images\\/themes\\/theme1\\/bg.png\", \"url\": null, \"description\": \"\\u0e2d\\u0e31\\u0e19\\u0e22\\u0e2d\\u0e07\\u0e04\\u0e48\\u0e30 !! \\u0e09\\u0e31\\u0e19\\u0e0b\\u0e39\\u0e08\\u0e35\\u0e27\\u0e07\\u0e21\\u0e34\\u0e2a\\u0e40\\u0e2d \\u0e09\\u0e31\\u0e19\\u0e23\\u0e31\\u0e48\\u0e27 \\u0e19\\u0e48\\u0e32\\u0e23\\u0e31\\u0e01 \\u0e25\\u0e2d\\u0e07\\u0e1f\\u0e2d\\u0e25\\u0e21\\u0e32\\u0e2a\\u0e34\\u0e04\\u0e48\\u0e30\\u0e41\\u0e25\\u0e49\\u0e27\\u0e04\\u0e38\\u0e13\\u0e08\\u0e30\\u0e23\\u0e39\\u0e49 *\\u0e22\\u0e34\\u0e49\\u0e21\\u0e2a\\u0e27\\u0e22*\", \"profile_sidebar_fill_color\": \"DDEEF6\", \"default_profile_image\": false, \"lang\": \"th\", \"favourites_count\": 2, \"profile_sidebar_border_color\": \"C0DEED\", \"profile_image_url_https\": \"https:\\/\\/si0.twimg.com\\/profile_images\\/2748527294\\/6cd82d00840820ddc2bedffdfb5e15bd_normal.jpeg\", \"location\": \"\\u0e43\\u0e19\\u0e04\\u0e48\\u0e32\\u0e22 JYP \\u0e04\\u0e48\\u0e30\", \"id_str\": \"700968576\", \"verified\": false, \"notifications\": null, \"protected\": false, \"screen_name\": \"suzy12missA\", \"following\": null, \"geo_enabled\": false, \"profile_use_background_image\": true, \"profile_image_url\": \"http:\\/\\/a0.twimg.com\\/profile_images\\/2748527294\\/6cd82d00840820ddc2bedffdfb5e15bd_normal.jpeg\", \"name\": \"\\u0e19\\u0e32\\u0e07\\u0e40\\u0e1a!!\\u0e0b\\u0e39\\u0e08\\u0e35^^\", \"profile_text_color\": \"333333\", \"id\": 700968576, \"listed_count\": 1, \"statuses_count\": 428, \"profile_background_image_url\": \"http:\\/\\/a0.twimg.com\\/images\\/themes\\/theme1\\/bg.png\", \"utc_offset\": null }, \"id\": 268680229562744832, \"possibly_sensitive_editable\": true}";
Status status = DataObjectFactory.createStatus(rawJSON);
URLEntity urlEntity = status.getURLEntities()[1];
assertEquals(urlEntity.getURL(), urlEntity.getDisplayURL());
assertEquals(urlEntity.getURL(), urlEntity.getExpandedURL());
}
List<Object[]> received = new ArrayList<Object[]>(3);
private synchronized void notifyResponse() {
this.notify();
}
public void onStatus(Status status) {
System.out.println("onStatus");
received.add(new Object[]{"onstatus", status});
Assert.assertNotNull(DataObjectFactory.getRawJSON(status));
notifyResponse();
}
public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
System.out.println("onDeletionNotice");
received.add(new Object[]{TwitterMethod.DESTROY_STATUS, statusDeletionNotice});
notifyResponse();
}
public void onDeletionNotice(long directMessageId, long userId) {
System.out.println("onDeletionNotice");
received.add(new Object[]{TwitterMethod.DESTROY_DIRECT_MESSAGE, directMessageId, userId});
notifyResponse();
}
public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
System.out.println("onTrackLimitationNotice");
received.add(new Object[]{"tracklimitation", numberOfLimitedStatuses});
notifyResponse();
}
public void onScrubGeo(long userId, long upToStatusId) {
System.out.println("onScrubGeo");
received.add(new Object[]{"scrubgeo", userId, upToStatusId});
notifyResponse();
}
@Override
public void onStallWarning(StallWarning warning) {
System.out.println("onStallWarning");
received.add(new Object[]{"stallwarning", warning});
notifyResponse();
}
public void onFriendList(long[] friendIds) {
System.out.println("onFriendList");
received.add(new Object[]{"onfriendlist", friendIds});
notifyResponse();
}
public void onFavorite(User source, User target, Status favoritedStatus) {
System.out.println("onFavorite");
received.add(new Object[]{TwitterMethod.CREATE_FAVORITE, source, target, favoritedStatus});
Assert.assertNotNull(DataObjectFactory.getRawJSON(source));
Assert.assertNotNull(DataObjectFactory.getRawJSON(target));
Assert.assertNotNull(DataObjectFactory.getRawJSON(favoritedStatus));
notifyResponse();
}
public void onUnfavorite(User source, User target, Status unfavoritedStatus) {
System.out.println("onUnfavorite");
received.add(new Object[]{TwitterMethod.DESTROY_FAVORITE, source, target, unfavoritedStatus});
Assert.assertNotNull(DataObjectFactory.getRawJSON(source));
Assert.assertNotNull(DataObjectFactory.getRawJSON(target));
Assert.assertNotNull(DataObjectFactory.getRawJSON(unfavoritedStatus));
notifyResponse();
}
public void onFollow(User source, User followedUser) {
System.out.println("onfollow");
this.source = source;
this.target = followedUser;
received.add(new Object[]{TwitterMethod.CREATE_FRIENDSHIP, source, followedUser});
Assert.assertNotNull(DataObjectFactory.getRawJSON(source));
Assert.assertNotNull(DataObjectFactory.getRawJSON(followedUser));
notifyResponse();
}
public void onDirectMessage(DirectMessage directMessage) {
System.out.println("onDirectMessage");
received.add(new Object[]{TwitterMethod.SEND_DIRECT_MESSAGE, directMessage});
Assert.assertNotNull(DataObjectFactory.getRawJSON(directMessage));
notifyResponse();
}
public void onUserListMemberAddition(User addedMember, User listOwner, UserList list) {
System.out.println("onUserListMemberAddition");
received.add(new Object[]{TwitterMethod.CREATE_LIST_MEMBER, addedMember, listOwner, list});
Assert.assertNotNull(DataObjectFactory.getRawJSON(addedMember));
Assert.assertNotNull(DataObjectFactory.getRawJSON(listOwner));
Assert.assertNotNull(DataObjectFactory.getRawJSON(list));
notifyResponse();
}
public void onUserListMemberDeletion(User deletedMember, User listOwner, UserList list) {
System.out.println("onUserListMemberDeletion");
received.add(new Object[]{TwitterMethod.DESTROY_LIST_MEMBER, deletedMember, listOwner, list});
Assert.assertNotNull(DataObjectFactory.getRawJSON(deletedMember));
Assert.assertNotNull(DataObjectFactory.getRawJSON(listOwner));
Assert.assertNotNull(DataObjectFactory.getRawJSON(list));
notifyResponse();
}
public void onUserListSubscription(User subscriber, User listOwner, UserList list) {
System.out.println("onUserListSubscription");
received.add(new Object[]{TwitterMethod.SUBSCRIBE_LIST, subscriber, listOwner, list});
Assert.assertNotNull(DataObjectFactory.getRawJSON(subscriber));
Assert.assertNotNull(DataObjectFactory.getRawJSON(listOwner));
Assert.assertNotNull(DataObjectFactory.getRawJSON(list));
notifyResponse();
}
public void onUserListUnsubscription(User subscriber, User listOwner, UserList list) {
System.out.println("onUserListUnsubscription");
received.add(new Object[]{TwitterMethod.UNSUBSCRIBE_LIST, subscriber, listOwner, list});
Assert.assertNotNull(DataObjectFactory.getRawJSON(subscriber));
Assert.assertNotNull(DataObjectFactory.getRawJSON(listOwner));
Assert.assertNotNull(DataObjectFactory.getRawJSON(list));
notifyResponse();
}
public void onUserListCreation(User listOwner, UserList list) {
System.out.println("onUserListCreation");
received.add(new Object[]{TwitterMethod.CREATE_USER_LIST, listOwner, list});
Assert.assertNotNull(DataObjectFactory.getRawJSON(listOwner));
Assert.assertNotNull(DataObjectFactory.getRawJSON(list));
notifyResponse();
}
public void onUserListUpdate(User listOwner, UserList list) {
System.out.println("onUserListUpdate");
received.add(new Object[]{TwitterMethod.UPDATE_USER_LIST, listOwner, list});
Assert.assertNotNull(DataObjectFactory.getRawJSON(listOwner));
Assert.assertNotNull(DataObjectFactory.getRawJSON(list));
notifyResponse();
}
public void onUserListDeletion(User listOwner, UserList list) {
System.out.println("onUserListDeletion");
received.add(new Object[]{TwitterMethod.DESTROY_USER_LIST, listOwner, list});
notifyResponse();
Assert.assertNotNull(DataObjectFactory.getRawJSON(listOwner));
Assert.assertNotNull(DataObjectFactory.getRawJSON(list));
}
public void onUserProfileUpdate(User updatedUser) {
System.out.println("onUserProfileUpdate");
received.add(new Object[]{TwitterMethod.UPDATE_PROFILE, updatedUser});
Assert.assertNotNull(DataObjectFactory.getRawJSON(updatedUser));
notifyResponse();
}
public void onBlock(User source, User blockedUser) {
System.out.println("onBlock");
received.add(new Object[]{TwitterMethod.CREATE_BLOCK, source, blockedUser});
Assert.assertNotNull(DataObjectFactory.getRawJSON(source));
Assert.assertNotNull(DataObjectFactory.getRawJSON(blockedUser));
notifyResponse();
}
public void onUnblock(User source, User unblockedUser) {
System.out.println("onUnblock");
received.add(new Object[]{TwitterMethod.DESTROY_BLOCK, source, unblockedUser});
Assert.assertNotNull(DataObjectFactory.getRawJSON(source));
Assert.assertNotNull(DataObjectFactory.getRawJSON(unblockedUser));
notifyResponse();
}
public void onException(Exception ex) {
System.out.println("onException");
received.add(new Object[]{ex});
ex.printStackTrace();
notifyResponse();
}
}